From owner-svn-src-all@freebsd.org Fri Aug 23 16:18:25 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 9BF1AC8F63; Fri, 23 Aug 2019 16:18:25 +0000 (UTC) (envelope-from emaste@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46FRQn4N6Lz3wdT; Fri, 23 Aug 2019 16:18:25 +0000 (UTC) (envelope-from emaste@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 5EA391F2DF; Fri, 23 Aug 2019 16:18:25 +0000 (UTC) (envelope-from emaste@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x7NGIP07098254; Fri, 23 Aug 2019 16:18:25 GMT (envelope-from emaste@FreeBSD.org) Received: (from emaste@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x7NGIPvQ098253; Fri, 23 Aug 2019 16:18:25 GMT (envelope-from emaste@FreeBSD.org) Message-Id: <201908231618.x7NGIPvQ098253@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: emaste set sender to emaste@FreeBSD.org using -f From: Ed Maste Date: Fri, 23 Aug 2019 16:18:25 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r351426 - stable/12/usr.bin/vtfontcvt X-SVN-Group: stable-12 X-SVN-Commit-Author: emaste X-SVN-Commit-Paths: stable/12/usr.bin/vtfontcvt X-SVN-Commit-Revision: 351426 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 23 Aug 2019 16:18:25 -0000 Author: emaste Date: Fri Aug 23 16:18:25 2019 New Revision: 351426 URL: https://svnweb.freebsd.org/changeset/base/351426 Log: vtfontcvt: improve BDF and hex font parsing MFC r348661: vtfontcvt: rework height/width setting Introduce VFNT_MAXDIMENSION to replace bare 128, add set_height, and consistently use set_height and set_width. Submitted by: Dmitry Wagin MFC r348662: vtfontcvt: include width and height in verbose info Submitted by: Dmitry Wagin MFC r348668: vtfontcvt: zero memory allocated by xmalloc Submitted by: Dmitry Wagin MFC r348692: vtfontcvt: exit on error if the input font has too many glyphs The kernel has a limit of 131072 glyphs in a font; add the same check to vtfontcvt so that we won't create a font file that the kernel will not load. MFC r348796: vtfontcvt: allow out-of-order glyphs Reported by: mi MFC r349049: vtfontcvt: add comments in add_glyph MFC r349100: vtfontcvt: improve BDF and hex font parsing Support larger font sizes. Submitted by: Dmitry Wagin (original version) MFC r349101: vtfontcvt: initialize bbwbytes to avoid GCC 4.2.1 warning MFC r349105: vtfontcvt: initialize another variable to quiet GCC warning I believe this case could be triggered by a broken .bdf font. MFC r349107: vtfontcvt: improve .bdf verification Previously we would crash if the BBX y-offset was outside of the font bounding box. Reported by: afl fuzzer MFC r349108: vtfontcvt: improve .bdf validation Previously if we had a BBX entry that had invalid values (e.g. bounding box outside of font bounding box) and failed sscanf (e.g., because it had fewer than four values) we skipped the BBX value validation and then triggered an assertion failure. Reported by: afl fuzzer MFC r349111: vtfontcvt: correct typo in hex parsing update Submitted by: Dmitry Wagin MFC r349333: vtfontcvt: improve .bdf validation Previously if we had a FONTBOUNDINGBOX or DWIDTH entry that had missing or invalid values and and failed sscanf, we would proceeded with partially initialized bounding box / device width variables. Reported by: afl fuzzer (FONTBOUNDINGBOX) PR: 205707 Sponsored by: The FreeBSD Foundation Modified: stable/12/usr.bin/vtfontcvt/vtfontcvt.c Directory Properties: stable/12/ (props changed) Modified: stable/12/usr.bin/vtfontcvt/vtfontcvt.c ============================================================================== --- stable/12/usr.bin/vtfontcvt/vtfontcvt.c Fri Aug 23 16:03:23 2019 (r351425) +++ stable/12/usr.bin/vtfontcvt/vtfontcvt.c Fri Aug 23 16:18:25 2019 (r351426) @@ -49,6 +49,8 @@ __FBSDID("$FreeBSD$"); #define VFNT_MAP_NORMAL_RH 1 #define VFNT_MAP_BOLD 2 #define VFNT_MAP_BOLD_RH 3 +#define VFNT_MAXGLYPHS 131072 +#define VFNT_MAXDIMENSION 128 static unsigned int width = 8, wbytes, height = 16; @@ -101,7 +103,7 @@ xmalloc(size_t size) { void *m; - if ((m = malloc(size)) == NULL) + if ((m = calloc(1, size)) == NULL) errx(1, "memory allocation failure"); return (m); } @@ -109,7 +111,7 @@ xmalloc(size_t size) static int add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx) { - struct mapping *mp; + struct mapping *mp, *mp_temp; struct mapping_list *ml; mapping_total++; @@ -120,10 +122,19 @@ add_mapping(struct glyph *gl, unsigned int c, unsigned mp->m_length = 0; ml = &maps[map_idx]; - if (TAILQ_LAST(ml, mapping_list) != NULL && - TAILQ_LAST(ml, mapping_list)->m_char >= c) - errx(1, "Bad ordering at character %u", c); - TAILQ_INSERT_TAIL(ml, mp, m_list); + if (TAILQ_LAST(ml, mapping_list) == NULL || + TAILQ_LAST(ml, mapping_list)->m_char < c) { + /* Common case: empty list or new char at end of list. */ + TAILQ_INSERT_TAIL(ml, mp, m_list); + } else { + /* Find insertion point for char; cannot be at end. */ + TAILQ_FOREACH(mp_temp, ml, m_list) { + if (mp_temp->m_char >= c) { + TAILQ_INSERT_BEFORE(mp_temp, mp, m_list); + break; + } + } + } map_count[map_idx]++; mapping_unique++; @@ -165,6 +176,7 @@ add_glyph(const uint8_t *bytes, unsigned int map_idx, glyph_total++; glyph_count[map_idx]++; + /* Return existing glyph if we have an identical one. */ hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH; SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) { if (memcmp(gl->g_data, bytes, wbytes * height) == 0) { @@ -173,6 +185,7 @@ add_glyph(const uint8_t *bytes, unsigned int map_idx, } } + /* Allocate new glyph. */ gl = xmalloc(sizeof *gl); gl->g_data = xmalloc(wbytes * height); memcpy(gl->g_data, bytes, wbytes * height); @@ -183,6 +196,8 @@ add_glyph(const uint8_t *bytes, unsigned int map_idx, SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash); glyph_unique++; + if (glyph_unique > VFNT_MAXGLYPHS) + errx(1, "too many glyphs (%u)", glyph_unique); return (gl); } @@ -208,112 +223,243 @@ add_char(unsigned curchar, unsigned map_idx, uint8_t * return (0); } - +/* + * Right-shift glyph row by _shift_ bits. Row _len_ bits wide, _size_ bytes. + */ static int -parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line, - unsigned int dwidth) +rshift_row(uint8_t *line, size_t size, size_t len, size_t shift) { - uint8_t *p; - unsigned int i, subline; + size_t d, s, i; + uint16_t t; - if (dwidth != width && dwidth != width * 2) - errx(1, "Bitmap with unsupported width %u!", dwidth); + assert(size > 0 && len > 0); + assert(size * 8 >= len); - /* Move pixel data right to simplify splitting double characters. */ - line >>= (howmany(dwidth, 8) * 8) - dwidth; + if (shift == 0) + return (0); - for (i = dwidth / width; i > 0; i--) { - p = (i == 2) ? right : left; + d = shift / 8; + s = 8 - shift % 8; + i = howmany(len, 8); - subline = line & ((1 << width) - 1); - subline <<= (howmany(width, 8) * 8) - width; + while (i > 0) { + i--; - if (wbytes == 1) { - *p = subline; - } else if (wbytes == 2) { - *p++ = subline >> 8; - *p = subline; - } else { - errx(1, "Unsupported wbytes %u!", wbytes); - } + t = *(line + i); + *(line + i) = 0; - line >>= width; + t <<= s; + + if (i + d + 1 < size) + *(line + i + d + 1) |= (uint8_t)t; + if (i + d < size) + *(line + i + d) = t >> 8; } + return (0); +} +/* + * Split double-width characters into left and right half. Single-width + * characters in _left_ only. + */ +static int +split_row(uint8_t *left, uint8_t *right, uint8_t *line, size_t w) +{ + size_t s, i; + + s = wbytes * 8 - width; + + memcpy(left, line, wbytes); + *(left + wbytes - 1) &= 0xFF << s; + + if (w > width) { /* Double-width character. */ + uint8_t t; + + for (i = 0; i < wbytes; i++) { + t = *(line + wbytes + i - 1); + t <<= 8 - s; + t |= *(line + wbytes + i) >> s; + *(right + i) = t; + } + *(right + wbytes - 1) &= 0xFF << s; + } return (0); } +static void +set_height(int h) +{ + if (h <= 0 || h > VFNT_MAXDIMENSION) + errx(1, "invalid height %d", h); + height = h; +} + +static void +set_width(int w) +{ + if (w <= 0 || w > VFNT_MAXDIMENSION) + errx(1, "invalid width %d", w); + width = w; + wbytes = howmany(width, 8); +} + static int parse_bdf(FILE *fp, unsigned int map_idx) { - char *ln; + char *line, *ln, *p; size_t length; - uint8_t bytes[wbytes * height], bytes_r[wbytes * height]; - unsigned int curchar = 0, dwidth = 0, i, line; + uint8_t *bytes, *bytes_r; + unsigned int curchar = 0, i, j, linenum = 0, bbwbytes; + int bbw, bbh, bbox, bboy; /* Glyph bounding box. */ + int fbbw = 0, fbbh, fbbox, fbboy; /* Font bounding box. */ + int dwidth = 0, dwy = 0; + int rv = -1; + char spc = '\0'; + /* + * Step 1: Parse FONT logical font descriptor and FONTBOUNDINGBOX + * bounding box. + */ while ((ln = fgetln(fp, &length)) != NULL) { + linenum++; ln[length - 1] = '\0'; - if (strncmp(ln, "ENCODING ", 9) == 0) { - curchar = atoi(ln + 9); + if (strncmp(ln, "FONT ", 5) == 0) { + p = ln + 5; + i = 0; + while ((p = strchr(p, '-')) != NULL) { + p++; + i++; + if (i == 11) { + spc = *p; + break; + } + } + } else if (strncmp(ln, "FONTBOUNDINGBOX ", 16) == 0) { + if (sscanf(ln + 16, "%d %d %d %d", &fbbw, &fbbh, &fbbox, + &fbboy) != 4) + errx(1, "invalid FONTBOUNDINGBOX at line %u", + linenum); + set_width(fbbw); + set_height(fbbh); + break; } + } + if (fbbw == 0) + errx(1, "broken font header"); + if (spc != 'c' && spc != 'C') + errx(1, "font spacing \"C\" (character cell) required"); + /* Step 2: Validate DWIDTH (Device Width) of all glyphs. */ + while ((ln = fgetln(fp, &length)) != NULL) { + linenum++; + ln[length - 1] = '\0'; + if (strncmp(ln, "DWIDTH ", 7) == 0) { - dwidth = atoi(ln + 7); + if (sscanf(ln + 7, "%d %d", &dwidth, &dwy) != 2) + errx(1, "invalid DWIDTH at line %u", linenum); + if (dwy != 0 || (dwidth != fbbw && dwidth * 2 != fbbw)) + errx(1, "bitmap with unsupported DWIDTH %d %d at line %u", + dwidth, dwy, linenum); + if (dwidth < fbbw) + set_width(dwidth); } + } - if (strncmp(ln, "BITMAP", 6) == 0 && + /* Step 3: Restart at the beginning of the file and read glyph data. */ + dwidth = bbw = bbh = 0; + rewind(fp); + linenum = 0; + bbwbytes = 0; /* GCC 4.2.1 "may be used uninitialized" workaround. */ + bytes = xmalloc(wbytes * height); + bytes_r = xmalloc(wbytes * height); + line = xmalloc(wbytes * 2); + while ((ln = fgetln(fp, &length)) != NULL) { + linenum++; + ln[length - 1] = '\0'; + + if (strncmp(ln, "ENCODING ", 9) == 0) { + curchar = atoi(ln + 9); + } else if (strncmp(ln, "DWIDTH ", 7) == 0) { + dwidth = atoi(ln + 7); + } else if (strncmp(ln, "BBX ", 4) == 0) { + if (sscanf(ln + 4, "%d %d %d %d", &bbw, &bbh, &bbox, + &bboy) != 4) + errx(1, "invalid BBX at line %u", linenum); + if (bbw < 1 || bbh < 1 || bbw > fbbw || bbh > fbbh || + bbox < fbbox || bboy < fbboy || + bbh + bboy > fbbh + fbboy) + errx(1, "broken bitmap with BBX %d %d %d %d at line %u", + bbw, bbh, bbox, bboy, linenum); + bbwbytes = howmany(bbw, 8); + } else if (strncmp(ln, "BITMAP", 6) == 0 && (ln[6] == ' ' || ln[6] == '\0')) { + if (dwidth == 0 || bbw == 0 || bbh == 0) + errx(1, "broken char header at line %u!", + linenum); + memset(bytes, 0, wbytes * height); + memset(bytes_r, 0, wbytes * height); + /* - * Assume that the next _height_ lines are bitmap - * data. ENDCHAR is allowed to terminate the bitmap + * Assume that the next _bbh_ lines are bitmap data. + * ENDCHAR is allowed to terminate the bitmap * early but is not otherwise checked; any extra data * is ignored. */ - for (i = 0; i < height; i++) { + for (i = (fbbh + fbboy) - (bbh + bboy); + i < (unsigned int)((fbbh + fbboy) - bboy); i++) { if ((ln = fgetln(fp, &length)) == NULL) - errx(1, "Unexpected EOF!"); + errx(1, "unexpected EOF"); + linenum++; ln[length - 1] = '\0'; - if (strcmp(ln, "ENDCHAR") == 0) { - memset(bytes + i * wbytes, 0, - (height - i) * wbytes); - memset(bytes_r + i * wbytes, 0, - (height - i) * wbytes); + if (strcmp(ln, "ENDCHAR") == 0) break; + if (strlen(ln) < bbwbytes * 2) + errx(1, "broken bitmap at line %u", + linenum); + memset(line, 0, wbytes * 2); + for (j = 0; j < bbwbytes; j++) { + unsigned int val; + if (sscanf(ln + j * 2, "%2x", &val) == + 0) + break; + *(line + j) = (uint8_t)val; } - sscanf(ln, "%x", &line); - if (parse_bitmap_line(bytes + i * wbytes, - bytes_r + i * wbytes, line, dwidth) != 0) - return (1); + + rv = rshift_row(line, wbytes * 2, bbw, + bbox - fbbox); + if (rv != 0) + goto out; + + rv = split_row(bytes + i * wbytes, + bytes_r + i * wbytes, line, dwidth); + if (rv != 0) + goto out; } - if (add_char(curchar, map_idx, bytes, - dwidth == width * 2 ? bytes_r : NULL) != 0) - return (1); + rv = add_char(curchar, map_idx, bytes, + dwidth > (int)width ? bytes_r : NULL); + if (rv != 0) + goto out; + + dwidth = bbw = bbh = 0; } } - return (0); +out: + free(bytes); + free(bytes_r); + free(line); + return (rv); } -static void -set_width(int w) -{ - - if (w <= 0 || w > 128) - errx(1, "invalid width %d", w); - width = w; - wbytes = howmany(width, 8); -} - static int parse_hex(FILE *fp, unsigned int map_idx) { char *ln, *p; - char fmt_str[8]; size_t length; - uint8_t *bytes = NULL, *bytes_r = NULL; - unsigned curchar = 0, i, line, chars_per_row, dwidth; + uint8_t *bytes = NULL, *bytes_r = NULL, *line = NULL; + unsigned curchar = 0, gwidth, gwbytes, i, j, chars_per_row; int rv = 0; while ((ln = fgetln(fp, &length)) != NULL) { @@ -322,7 +468,7 @@ parse_hex(FILE *fp, unsigned int map_idx) if (strncmp(ln, "# Height: ", 10) == 0) { if (bytes != NULL) errx(1, "malformed input: Height tag after font data"); - height = atoi(ln + 10); + set_height(atoi(ln + 10)); } else if (strncmp(ln, "# Width: ", 9) == 0) { if (bytes != NULL) errx(1, "malformed input: Width tag after font data"); @@ -331,36 +477,46 @@ parse_hex(FILE *fp, unsigned int map_idx) if (bytes == NULL) { bytes = xmalloc(wbytes * height); bytes_r = xmalloc(wbytes * height); + line = xmalloc(wbytes * 2); } /* ln is guaranteed to have a colon here. */ p = strchr(ln, ':') + 1; chars_per_row = strlen(p) / height; - dwidth = width; - if (chars_per_row / 2 > (width + 7) / 8) - dwidth *= 2; /* Double-width character. */ - snprintf(fmt_str, sizeof(fmt_str), "%%%ux", - chars_per_row); + if (chars_per_row < wbytes * 2) + errx(1, + "malformed input: broken bitmap, character %06x", + curchar); + gwidth = width * 2; + gwbytes = howmany(gwidth, 8); + if (chars_per_row < gwbytes * 2 || gwidth <= 8) { + gwidth = width; /* Single-width character. */ + gwbytes = wbytes; + } for (i = 0; i < height; i++) { - sscanf(p, fmt_str, &line); - p += chars_per_row; - if (parse_bitmap_line(bytes + i * wbytes, - bytes_r + i * wbytes, line, dwidth) != 0) { - rv = 1; - goto out; + for (j = 0; j < gwbytes; j++) { + unsigned int val; + if (sscanf(p + j * 2, "%2x", &val) == 0) + break; + *(line + j) = (uint8_t)val; } + rv = split_row(bytes + i * wbytes, + bytes_r + i * wbytes, line, gwidth); + if (rv != 0) + goto out; + p += gwbytes * 2; } - if (add_char(curchar, map_idx, bytes, - dwidth == width * 2 ? bytes_r : NULL) != 0) { - rv = 1; + rv = add_char(curchar, map_idx, bytes, + gwidth != width ? bytes_r : NULL); + if (rv != 0) goto out; - } } } out: free(bytes); free(bytes_r); + free(line); return (rv); } @@ -512,6 +668,8 @@ print_font_info(void) { printf( "Statistics:\n" +"- width: %6u\n" +"- height: %6u\n" "- glyph_total: %6u\n" "- glyph_normal: %6u\n" "- glyph_normal_right: %6u\n" @@ -530,6 +688,7 @@ print_font_info(void) "- mapping_bold_right_folded: %6u\n" "- mapping_unique: %6u\n" "- mapping_dupe: %6u\n", + width, height, glyph_total, glyph_count[0], glyph_count[1], @@ -547,7 +706,7 @@ print_font_info(void) int main(int argc, char *argv[]) { - int ch, val, verbose = 0; + int ch, verbose = 0; assert(sizeof(struct file_header) == 32); assert(sizeof(struct file_mapping) == 8); @@ -555,16 +714,13 @@ main(int argc, char *argv[]) while ((ch = getopt(argc, argv, "h:vw:")) != -1) { switch (ch) { case 'h': - val = atoi(optarg); - if (val <= 0 || val > 128) - errx(1, "Invalid height %d", val); - height = val; + height = atoi(optarg); break; case 'v': verbose = 1; break; case 'w': - set_width(atoi(optarg)); + width = atoi(optarg); break; case '?': default: @@ -577,7 +733,8 @@ main(int argc, char *argv[]) if (argc < 2 || argc > 3) usage(); - wbytes = howmany(width, 8); + set_width(width); + set_height(height); if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0) return (1);