Fix about ten integer overflows and underflows and a handful of logic

errors in line number handling.

Submitted by:	ingo at OpenBSD
Discussed with:	ingo at OpenBSD
Obtained from:	OpenBSD
This commit is contained in:
Baptiste Daroussin 2015-05-08 22:11:54 +00:00
parent 5791e134f7
commit f23ed79b08
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=282669

View File

@ -96,6 +96,7 @@ struct line_str {
int l_max_col; /* max column in the line */ int l_max_col; /* max column in the line */
}; };
static void addto_lineno(int *, int);
static LINE *alloc_line(void); static LINE *alloc_line(void);
static void dowarn(int); static void dowarn(int);
static void flush_line(LINE *); static void flush_line(LINE *);
@ -108,7 +109,7 @@ static CSET last_set; /* char_set of last char printed */
static LINE *lines; static LINE *lines;
static int compress_spaces; /* if doing space -> tab conversion */ static int compress_spaces; /* if doing space -> tab conversion */
static int fine; /* if `fine' resolution (half lines) */ static int fine; /* if `fine' resolution (half lines) */
static int max_bufd_lines; /* max # lines to keep in memory */ static int max_bufd_lines; /* max # of half lines to keep in memory */
static int nblank_lines; /* # blanks after last flushed line */ static int nblank_lines; /* # blanks after last flushed line */
static int no_backspaces; /* if not to output any backspaces */ static int no_backspaces; /* if not to output any backspaces */
static int pass_unknown_seqs; /* pass unknown control sequences */ static int pass_unknown_seqs; /* pass unknown control sequences */
@ -133,6 +134,7 @@ main(int argc, char **argv)
int this_line; /* line l points to */ int this_line; /* line l points to */
int nflushd_lines; /* number of lines that were flushed */ int nflushd_lines; /* number of lines that were flushed */
int adjust, opt, warned, width; int adjust, opt, warned, width;
const char *errstr;
cap_rights_t rights; cap_rights_t rights;
unsigned long cmd; unsigned long cmd;
@ -151,7 +153,7 @@ main(int argc, char **argv)
if (cap_enter() < 0 && errno != ENOSYS) if (cap_enter() < 0 && errno != ENOSYS)
err(1, "unable to enter capability mode"); err(1, "unable to enter capability mode");
max_bufd_lines = 128; max_bufd_lines = 256;
compress_spaces = 1; /* compress spaces into tabs */ compress_spaces = 1; /* compress spaces into tabs */
while ((opt = getopt(argc, argv, "bfhl:px")) != -1) while ((opt = getopt(argc, argv, "bfhl:px")) != -1)
switch (opt) { switch (opt) {
@ -165,8 +167,11 @@ main(int argc, char **argv)
compress_spaces = 1; compress_spaces = 1;
break; break;
case 'l': /* buffered line count */ case 'l': /* buffered line count */
if ((max_bufd_lines = atoi(optarg)) <= 0) max_bufd_lines = strtonum(optarg, 1,
errx(1, "bad -l argument %s", optarg); (INT_MAX - BUFFER_MARGIN) / 2, &errstr) * 2;
if (errstr != NULL)
errx(1, "bad -l argument, %s: %s", errstr,
optarg);
break; break;
case 'p': /* pass unknown control sequences */ case 'p': /* pass unknown control sequences */
pass_unknown_seqs = 1; pass_unknown_seqs = 1;
@ -182,9 +187,6 @@ main(int argc, char **argv)
if (optind != argc) if (optind != argc)
usage(); usage();
/* this value is in half lines */
max_bufd_lines *= 2;
adjust = cur_col = extra_lines = warned = 0; adjust = cur_col = extra_lines = warned = 0;
cur_line = max_line = nflushd_lines = this_line = 0; cur_line = max_line = nflushd_lines = this_line = 0;
cur_set = last_set = CS_NORMAL; cur_set = last_set = CS_NORMAL;
@ -204,19 +206,19 @@ main(int argc, char **argv)
case ESC: /* just ignore EOF */ case ESC: /* just ignore EOF */
switch(getwchar()) { switch(getwchar()) {
case RLF: case RLF:
cur_line -= 2; addto_lineno(&cur_line, -2);
break; break;
case RHLF: case RHLF:
cur_line--; addto_lineno(&cur_line, -1);
break; break;
case FHLF: case FHLF:
cur_line++; addto_lineno(&cur_line, 1);
if (cur_line > max_line) if (cur_line > max_line)
max_line = cur_line; max_line = cur_line;
} }
continue; continue;
case NL: case NL:
cur_line += 2; addto_lineno(&cur_line, 2);
if (cur_line > max_line) if (cur_line > max_line)
max_line = cur_line; max_line = cur_line;
cur_col = 0; cur_col = 0;
@ -235,7 +237,7 @@ main(int argc, char **argv)
++cur_col; ++cur_col;
continue; continue;
case VT: case VT:
cur_line -= 2; addto_lineno(&cur_line, -2);
continue; continue;
} }
if (iswspace(ch)) { if (iswspace(ch)) {
@ -248,58 +250,61 @@ main(int argc, char **argv)
} }
/* Must stuff ch in a line - are we at the right one? */ /* Must stuff ch in a line - are we at the right one? */
if (cur_line != this_line - adjust) { if (cur_line + adjust != this_line) {
LINE *lnew; LINE *lnew;
int nmove;
adjust = 0; /* round up to next line */
nmove = cur_line - this_line; adjust = !fine && (cur_line & 1);
if (!fine) {
/* round up to next line */ if (cur_line + adjust < this_line) {
if (cur_line & 1) { while (cur_line + adjust < this_line &&
adjust = 1; l->l_prev != NULL) {
nmove++;
}
}
if (nmove < 0) {
for (; nmove < 0 && l->l_prev; nmove++)
l = l->l_prev; l = l->l_prev;
if (nmove) { this_line--;
}
if (cur_line + adjust < this_line) {
if (nflushd_lines == 0) { if (nflushd_lines == 0) {
/* /*
* Allow backup past first * Allow backup past first
* line if nothing has been * line if nothing has been
* flushed yet. * flushed yet.
*/ */
for (; nmove < 0; nmove++) { while (cur_line + adjust
< this_line) {
lnew = alloc_line(); lnew = alloc_line();
l->l_prev = lnew; l->l_prev = lnew;
lnew->l_next = l; lnew->l_next = l;
l = lines = lnew; l = lines = lnew;
extra_lines++; extra_lines++;
this_line--;
} }
} else { } else {
if (!warned++) if (!warned++)
dowarn(cur_line); dowarn(cur_line);
cur_line -= nmove; cur_line = this_line - adjust;
} }
} }
} else { } else {
/* may need to allocate here */ /* may need to allocate here */
for (; nmove > 0 && l->l_next; nmove--) while (cur_line + adjust > this_line) {
if (l->l_next == NULL) {
l->l_next = alloc_line();
l->l_next->l_prev = l;
}
l = l->l_next; l = l->l_next;
for (; nmove > 0; nmove--) { this_line++;
lnew = alloc_line();
lnew->l_prev = l;
l->l_next = lnew;
l = lnew;
} }
} }
this_line = cur_line + adjust; if (this_line > nflushd_lines &&
nmove = this_line - nflushd_lines; this_line - nflushd_lines >=
if (nmove >= max_bufd_lines + BUFFER_MARGIN) { max_bufd_lines + BUFFER_MARGIN) {
nflushd_lines += nmove - max_bufd_lines; if (extra_lines) {
flush_lines(nmove - max_bufd_lines); flush_lines(extra_lines);
extra_lines = 0;
}
flush_lines(this_line - nflushd_lines -
max_bufd_lines);
nflushd_lines = this_line - max_bufd_lines;
} }
} }
/* grow line's buffer? */ /* grow line's buffer? */
@ -330,25 +335,23 @@ main(int argc, char **argv)
} }
if (ferror(stdin)) if (ferror(stdin))
err(1, NULL); err(1, NULL);
if (max_line == 0) if (extra_lines)
exit(0); /* no lines, so just exit */ flush_lines(extra_lines);
/* goto the last line that had a character on it */ /* goto the last line that had a character on it */
for (; l->l_next; l = l->l_next) for (; l->l_next; l = l->l_next)
this_line++; this_line++;
flush_lines(this_line - nflushd_lines + extra_lines + 1); flush_lines(this_line - nflushd_lines + 1);
/* make sure we leave things in a sane state */ /* make sure we leave things in a sane state */
if (last_set != CS_NORMAL) if (last_set != CS_NORMAL)
PUTC(SI); PUTC(SI);
/* flush out the last few blank lines */ /* flush out the last few blank lines */
nblank_lines = max_line - this_line; if (max_line > this_line)
nblank_lines = max_line - this_line;
if (max_line & 1) if (max_line & 1)
nblank_lines++; nblank_lines++;
else if (!nblank_lines)
/* missing a \n on the last line? */
nblank_lines = 2;
flush_blanks(); flush_blanks();
exit(0); exit(0);
} }
@ -365,7 +368,8 @@ flush_lines(int nflush)
flush_blanks(); flush_blanks();
flush_line(l); flush_line(l);
} }
nblank_lines++; if (l->l_line || l->l_next)
nblank_lines++;
if (l->l_line) if (l->l_line)
(void)free(l->l_line); (void)free(l->l_line);
free_line(l); free_line(l);
@ -517,6 +521,23 @@ flush_line(LINE *l)
} }
} }
/*
* Increment or decrement a line number, checking for overflow.
* Stop one below INT_MAX such that the adjust variable is safe.
*/
void
addto_lineno(int *lno, int offset)
{
if (offset > 0) {
if (*lno >= INT_MAX - offset)
errx(1, "too many lines");
} else {
if (*lno < INT_MIN - offset)
errx(1, "too many reverse line feeds");
}
*lno += offset;
}
#define NALLOC 64 #define NALLOC 64
static LINE *line_freelist; static LINE *line_freelist;