aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/vtfontcvt
diff options
context:
space:
mode:
authorEd Maste <emaste@FreeBSD.org>2019-08-23 16:18:25 +0000
committerEd Maste <emaste@FreeBSD.org>2019-08-23 16:18:25 +0000
commit863cb09ebbe2385861ae6bde02be3b4b2d91254e (patch)
tree933e79a40cd23bfbf769d4c71bfb1848cbf45771 /usr.bin/vtfontcvt
parent8145b790b3f5fca499f0745f14890aa9d50d9e8c (diff)
downloadsrc-863cb09ebbe2385861ae6bde02be3b4b2d91254e.tar.gz
src-863cb09ebbe2385861ae6bde02be3b4b2d91254e.zip
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
Notes
Notes: svn path=/stable/12/; revision=351426
Diffstat (limited to 'usr.bin/vtfontcvt')
-rw-r--r--usr.bin/vtfontcvt/vtfontcvt.c335
1 files changed, 246 insertions, 89 deletions
diff --git a/usr.bin/vtfontcvt/vtfontcvt.c b/usr.bin/vtfontcvt/vtfontcvt.c
index 351e37ea8a60..e34308d5d365 100644
--- a/usr.bin/vtfontcvt/vtfontcvt.c
+++ b/usr.bin/vtfontcvt/vtfontcvt.c
@@ -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 int map_idx)
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, int fallback)
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, int fallback)
}
}
+ /* 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, int fallback)
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 *bytes, uint8_t *bytes_r)
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;
+
+ t <<= s;
- line >>= width;
+ 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);
-}
-
-static void
-set_width(int w)
-{
-
- if (w <= 0 || w > 128)
- errx(1, "invalid width %d", w);
- width = w;
- wbytes = howmany(width, 8);
+out:
+ free(bytes);
+ free(bytes_r);
+ free(line);
+ return (rv);
}
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);