diff options
author | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2015-05-08 12:30:28 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2015-05-08 12:30:28 +0000 |
commit | b075b3218d95603f2ceb05f08c482c43d6c86231 (patch) | |
tree | 294e3e0c7e6518b179ffd370727004dee115f9e8 /usr.bin/col | |
parent | b0aa2a1ff4f535b1c42884d2dc1142cc6b9402ba (diff) |
Fix about ten integer overflows and underflows and a handful of logic
errors in line number handling. Detailed explanations were sent to tech@
on October 18, 2014.
OK doug@, and bapt at FreeBSD says he likes the direction
Diffstat (limited to 'usr.bin/col')
-rw-r--r-- | usr.bin/col/col.c | 114 |
1 files changed, 66 insertions, 48 deletions
diff --git a/usr.bin/col/col.c b/usr.bin/col/col.c index 42f4c61f97c..efceb690cba 100644 --- a/usr.bin/col/col.c +++ b/usr.bin/col/col.c @@ -1,4 +1,4 @@ -/* $OpenBSD: col.c,v 1.14 2014/10/17 21:27:10 schwarze Exp $ */ +/* $OpenBSD: col.c,v 1.15 2015/05/08 12:30:27 schwarze Exp $ */ /* $NetBSD: col.c,v 1.7 1995/09/02 05:48:50 jtc Exp $ */ /*- @@ -78,6 +78,7 @@ struct line_str { int l_needs_sort; /* set if chars went in out of order */ }; +void addto_lineno(int *, int); LINE *alloc_line(void); void dowarn(int); void flush_line(LINE *); @@ -91,7 +92,7 @@ CSET last_set; /* char_set of last char printed */ LINE *lines; int compress_spaces; /* if doing space -> tab conversion */ int fine; /* if `fine' resolution (half lines) */ -int max_bufd_lines; /* max # lines to keep in memory */ +int max_bufd_lines; /* max # of half lines to keep in memory */ int nblank_lines; /* # blanks after last flushed line */ int no_backspaces; /* if not to output any backspaces */ @@ -115,7 +116,7 @@ main(int argc, char *argv[]) int adjust, opt, warned; const char *errstr; - max_bufd_lines = 128; + max_bufd_lines = 256; compress_spaces = 1; /* compress spaces into tabs */ while ((opt = getopt(argc, argv, "bfhl:x")) != -1) switch (opt) { @@ -129,7 +130,8 @@ main(int argc, char *argv[]) compress_spaces = 1; break; case 'l': /* buffered line count */ - max_bufd_lines = strtonum(optarg, 1, INT_MAX, &errstr); + max_bufd_lines = strtonum(optarg, 1, + (INT_MAX - BUFFER_MARGIN) / 2, &errstr) * 2; if (errstr != NULL) errx(1, "bad -l argument, %s: %s", errstr, optarg); @@ -145,9 +147,6 @@ main(int argc, char *argv[]) if (optind != argc) usage(); - /* this value is in half lines */ - max_bufd_lines *= 2; - adjust = extra_lines = warned = 0; cur_col = 0; cur_line = max_line = nflushd_lines = this_line = 0; @@ -168,19 +167,19 @@ main(int argc, char *argv[]) case ESC: /* just ignore EOF */ switch(getchar()) { case RLF: - cur_line -= 2; + addto_lineno(&cur_line, -2); break; case RHLF: - cur_line--; + addto_lineno(&cur_line, -1); break; case FHLF: - cur_line++; + addto_lineno(&cur_line, 1); if (cur_line > max_line) max_line = cur_line; } continue; case NL: - cur_line += 2; + addto_lineno(&cur_line, 2); if (cur_line > max_line) max_line = cur_line; cur_col = 0; @@ -199,65 +198,68 @@ main(int argc, char *argv[]) ++cur_col; continue; case VT: - cur_line -= 2; + addto_lineno(&cur_line, -2); continue; } continue; } /* 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; - int nmove; - - adjust = 0; - nmove = cur_line - this_line; - if (!fine) { - /* round up to next line */ - if (cur_line & 1) { - adjust = 1; - nmove++; - } - } - if (nmove < 0) { - for (; nmove < 0 && l->l_prev; nmove++) + + /* round up to next line */ + adjust = !fine && (cur_line & 1); + + if (cur_line + adjust < this_line) { + while (cur_line + adjust < this_line && + l->l_prev != NULL) { l = l->l_prev; - if (nmove) { + this_line--; + } + if (cur_line + adjust < this_line) { if (nflushd_lines == 0) { /* * Allow backup past first * line if nothing has been * flushed yet. */ - for (; nmove < 0; nmove++) { + while (cur_line + adjust + < this_line) { lnew = alloc_line(); l->l_prev = lnew; lnew->l_next = l; l = lines = lnew; extra_lines++; + this_line--; } } else { if (!warned++) dowarn(cur_line); - cur_line -= nmove; + cur_line = this_line - adjust; } } } else { /* 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; - for (; nmove > 0; nmove--) { - lnew = alloc_line(); - lnew->l_prev = l; - l->l_next = lnew; - l = lnew; + this_line++; } } - this_line = cur_line + adjust; - nmove = this_line - nflushd_lines; - if (nmove >= max_bufd_lines + BUFFER_MARGIN) { - nflushd_lines += nmove - max_bufd_lines; - flush_lines(nmove - max_bufd_lines); + if (this_line > nflushd_lines && + this_line - nflushd_lines >= + max_bufd_lines + BUFFER_MARGIN) { + if (extra_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? */ @@ -283,25 +285,23 @@ main(int argc, char *argv[]) l->l_max_col = cur_col; cur_col++; } - if (max_line == 0) - exit(0); /* no lines, so just exit */ + if (extra_lines) + flush_lines(extra_lines); /* goto the last line that had a character on it */ for (; l->l_next; l = l->l_next) 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 */ if (last_set != CS_NORMAL) PUTC('\017'); /* 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) nblank_lines++; - else if (!nblank_lines) - /* missing a \n on the last line? */ - nblank_lines = 2; flush_blanks(); exit(0); } @@ -318,7 +318,8 @@ flush_lines(int nflush) flush_blanks(); flush_line(l); } - nblank_lines++; + if (l->l_line || l->l_next) + nblank_lines++; if (l->l_line) (void)free((void *)l->l_line); free_line(l); @@ -457,6 +458,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 static LINE *line_freelist; |