Bug Summary

File:src/usr.bin/col/col.c
Warning:line 427, column 14
Access to field 'c_column' results in a dereference of a null pointer (loaded from variable 'c')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name col.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/col/obj -resource-dir /usr/local/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/col/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/col/col.c
1/* $OpenBSD: col.c,v 1.20 2022/12/04 23:50:47 cheloha Exp $ */
2/* $NetBSD: col.c,v 1.7 1995/09/02 05:48:50 jtc Exp $ */
3
4/*-
5 * Copyright (c) 1990, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Michael Rendell of the Memorial University of Newfoundland.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <ctype.h>
37#include <err.h>
38#include <string.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <limits.h>
43
44#define BS'\b' '\b' /* backspace */
45#define TAB'\t' '\t' /* tab */
46#define SPACE' ' ' ' /* space */
47#define NL'\n' '\n' /* newline */
48#define CR'\r' '\r' /* carriage return */
49#define ESC'\033' '\033' /* escape */
50#define SI'\017' '\017' /* shift in to normal character set */
51#define SO'\016' '\016' /* shift out to alternate character set */
52#define VT'\013' '\013' /* vertical tab (aka reverse line feed) */
53
54/* build up at least this many lines before flushing them out */
55#define BUFFER_MARGIN32 32
56
57typedef char CSET;
58
59typedef struct char_str {
60#define CS_NORMAL1 1
61#define CS_ALTERNATE2 2
62 size_t c_column; /* column character is in */
63 CSET c_set; /* character set (currently only 2) */
64 char c_char; /* character in question */
65} CHAR;
66
67typedef struct line_str LINE;
68struct line_str {
69 CHAR *l_line; /* characters on the line */
70 LINE *l_prev; /* previous line */
71 LINE *l_next; /* next line */
72 size_t l_lsize; /* allocated sizeof l_line */
73 size_t l_line_len; /* strlen(l_line) */
74 size_t l_max_col; /* max column in the line */
75 int l_needs_sort; /* set if chars went in out of order */
76};
77
78void addto_lineno(int *, int);
79LINE *alloc_line(void);
80void dowarn(int);
81void flush_line(LINE *);
82void flush_lines(int);
83void flush_blanks(void);
84void free_line(LINE *);
85void usage(void);
86void *xreallocarray(void *, size_t, size_t);
87
88CSET last_set; /* char_set of last char printed */
89LINE *lines;
90int compress_spaces; /* if doing space -> tab conversion */
91int fine; /* if `fine' resolution (half lines) */
92int max_bufd_lines; /* max # of half lines to keep in memory */
93int nblank_lines; /* # blanks after last flushed line */
94int no_backspaces; /* if not to output any backspaces */
95
96#define PUTC(ch)if ((!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch,
(&__sF[1]))) == (-1)) err(1, "stdout");
\
97 if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (&
__sF[1])))
== EOF(-1)) \
98 err(1, "stdout");
99
100int
101main(int argc, char *argv[])
102{
103 int ch;
104 CHAR *c;
105 CSET cur_set; /* current character set */
106 LINE *l; /* current line */
107 int extra_lines; /* # of lines above first line */
108 size_t cur_col; /* current column */
109 int cur_line; /* line number of current position */
110 int max_line; /* max value of cur_line */
111 int this_line; /* line l points to */
112 int nflushd_lines; /* number of lines that were flushed */
113 int adjust, opt, warned;
114 const char *errstr;
115
116 if (pledge("stdio", NULL((void *)0)) == -1)
1
Assuming the condition is false
2
Taking false branch
117 err(1, "pledge");
118
119 max_bufd_lines = 256;
120 compress_spaces = 1; /* compress spaces into tabs */
121 while ((opt = getopt(argc, argv, "bfhl:x")) != -1)
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 146
122 switch (opt) {
123 case 'b': /* do not output backspaces */
124 no_backspaces = 1;
125 break;
126 case 'f': /* allow half forward line feeds */
127 fine = 1;
128 break;
129 case 'h': /* compress spaces into tabs */
130 compress_spaces = 1;
131 break;
132 case 'l': /* buffered line count */
133 max_bufd_lines = strtonum(optarg, 1,
134 (INT_MAX0x7fffffff - BUFFER_MARGIN32) / 2, &errstr) * 2;
135 if (errstr != NULL((void *)0))
136 errx(1, "bad -l argument, %s: %s", errstr,
137 optarg);
138 break;
139 case 'x': /* do not compress spaces into tabs */
140 compress_spaces = 0;
141 break;
142 default:
143 usage();
144 }
145
146 if (optind != argc)
5
Assuming 'optind' is equal to 'argc'
6
Taking false branch
147 usage();
148
149 adjust = extra_lines = warned = 0;
150 cur_col = 0;
151 cur_line = max_line = nflushd_lines = this_line = 0;
152 cur_set = last_set = CS_NORMAL1;
153 lines = l = alloc_line();
154
155 while ((ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) != EOF(-1)
) {
7
Assuming '__isthreaded' is not equal to 0
8
'?' condition is false
9
Assuming the condition is false
10
Loop condition is false. Execution continues on line 298
156 if (!isgraph(ch)) {
157 switch (ch) {
158 case BS'\b': /* can't go back further */
159 if (cur_col == 0)
160 continue;
161 --cur_col;
162 continue;
163 case CR'\r':
164 cur_col = 0;
165 continue;
166 case ESC'\033': /* just ignore EOF */
167 /*
168 * In the input stream, accept both the
169 * XPG5 sequences ESC-digit and the
170 * traditional BSD sequences ESC-ctrl.
171 */
172 switch(getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) {
173 case '7': /* reverse line feed */
174 /* FALLTHROUGH */
175 case '\007':
176 addto_lineno(&cur_line, -2);
177 break;
178 case '8': /* reverse half-line feed */
179 /* FALLTHROUGH */
180 case '\010':
181 addto_lineno(&cur_line, -1);
182 break;
183 case '9': /* forward half-line feed */
184 /* FALLTHROUGH */
185 case '\011':
186 addto_lineno(&cur_line, 1);
187 if (cur_line > max_line)
188 max_line = cur_line;
189 }
190 continue;
191 case NL'\n':
192 addto_lineno(&cur_line, 2);
193 if (cur_line > max_line)
194 max_line = cur_line;
195 cur_col = 0;
196 continue;
197 case SPACE' ':
198 ++cur_col;
199 continue;
200 case SI'\017':
201 cur_set = CS_NORMAL1;
202 continue;
203 case SO'\016':
204 cur_set = CS_ALTERNATE2;
205 continue;
206 case TAB'\t': /* adjust column */
207 cur_col |= 7;
208 ++cur_col;
209 continue;
210 case VT'\013':
211 addto_lineno(&cur_line, -2);
212 continue;
213 }
214 continue;
215 }
216
217 /* Must stuff ch in a line - are we at the right one? */
218 if (cur_line + adjust != this_line) {
219 LINE *lnew;
220
221 /* round up to next line */
222 adjust = !fine && (cur_line & 1);
223
224 if (cur_line + adjust < this_line) {
225 while (cur_line + adjust < this_line &&
226 l->l_prev != NULL((void *)0)) {
227 l = l->l_prev;
228 this_line--;
229 }
230 if (cur_line + adjust < this_line) {
231 if (nflushd_lines == 0) {
232 /*
233 * Allow backup past first
234 * line if nothing has been
235 * flushed yet.
236 */
237 while (cur_line + adjust
238 < this_line) {
239 lnew = alloc_line();
240 l->l_prev = lnew;
241 lnew->l_next = l;
242 l = lines = lnew;
243 extra_lines++;
244 this_line--;
245 }
246 } else {
247 if (!warned++)
248 dowarn(cur_line);
249 cur_line = this_line - adjust;
250 }
251 }
252 } else {
253 /* may need to allocate here */
254 while (cur_line + adjust > this_line) {
255 if (l->l_next == NULL((void *)0)) {
256 l->l_next = alloc_line();
257 l->l_next->l_prev = l;
258 }
259 l = l->l_next;
260 this_line++;
261 }
262 }
263 if (this_line > nflushd_lines &&
264 this_line - nflushd_lines >=
265 max_bufd_lines + BUFFER_MARGIN32) {
266 if (extra_lines) {
267 flush_lines(extra_lines);
268 extra_lines = 0;
269 }
270 flush_lines(this_line - nflushd_lines -
271 max_bufd_lines);
272 nflushd_lines = this_line - max_bufd_lines;
273 }
274 }
275 /* grow line's buffer? */
276 if (l->l_line_len + 1 >= l->l_lsize) {
277 size_t need;
278
279 need = l->l_lsize ? l->l_lsize : 45;
280 l->l_line = xreallocarray(l->l_line,
281 need, 2 * sizeof(CHAR));
282 l->l_lsize = need * 2;
283 }
284 c = &l->l_line[l->l_line_len++];
285 c->c_char = ch;
286 c->c_set = cur_set;
287 c->c_column = cur_col;
288 /*
289 * If things are put in out of order, they will need sorting
290 * when it is flushed.
291 */
292 if (cur_col < l->l_max_col)
293 l->l_needs_sort = 1;
294 else
295 l->l_max_col = cur_col;
296 cur_col++;
297 }
298 if (extra_lines
10.1
'extra_lines' is 0
)
11
Taking false branch
299 flush_lines(extra_lines);
300
301 /* goto the last line that had a character on it */
302 for (; l->l_next; l = l->l_next)
12
Loop condition is false. Execution continues on line 304
303 this_line++;
304 flush_lines(this_line - nflushd_lines + 1);
13
Calling 'flush_lines'
305
306 /* make sure we leave things in a sane state */
307 if (last_set != CS_NORMAL1)
308 PUTC(SI)if ((!__isthreaded ? __sputc('\017', (&__sF[1])) : (putc)
('\017', (&__sF[1]))) == (-1)) err(1, "stdout");
;
309
310 /* flush out the last few blank lines */
311 if (max_line > this_line)
312 nblank_lines = max_line - this_line;
313 if (max_line & 1)
314 nblank_lines++;
315 flush_blanks();
316 exit(0);
317}
318
319void
320flush_lines(int nflush)
321{
322 LINE *l;
323
324 while (--nflush >= 0) {
14
Loop condition is true. Entering loop body
325 l = lines;
326 lines = l->l_next;
327 if (l->l_line) {
15
Assuming field 'l_line' is non-null
16
Taking true branch
328 flush_blanks();
329 flush_line(l);
17
Calling 'flush_line'
330 }
331 if (l->l_line || l->l_next)
332 nblank_lines++;
333 if (l->l_line)
334 (void)free((void *)l->l_line);
335 free_line(l);
336 }
337 if (lines)
338 lines->l_prev = NULL((void *)0);
339}
340
341/*
342 * Print a number of newline/half newlines. If fine flag is set, nblank_lines
343 * is the number of half line feeds, otherwise it is the number of whole line
344 * feeds.
345 */
346void
347flush_blanks(void)
348{
349 int half, i, nb;
350
351 half = 0;
352 nb = nblank_lines;
353 if (nb & 1) {
354 if (fine)
355 half = 1;
356 else
357 nb++;
358 }
359 nb /= 2;
360 for (i = nb; --i >= 0;)
361 PUTC('\n')if ((!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n'
, (&__sF[1]))) == (-1)) err(1, "stdout");
;
362 if (half) {
363 /*
364 * In the output stream, always generate
365 * escape sequences conforming to XPG5.
366 */
367 PUTC(ESC)if ((!__isthreaded ? __sputc('\033', (&__sF[1])) : (putc)
('\033', (&__sF[1]))) == (-1)) err(1, "stdout");
;
368 PUTC('9')if ((!__isthreaded ? __sputc('9', (&__sF[1])) : (putc)('9'
, (&__sF[1]))) == (-1)) err(1, "stdout");
;
369 if (!nb)
370 PUTC('\r')if ((!__isthreaded ? __sputc('\r', (&__sF[1])) : (putc)('\r'
, (&__sF[1]))) == (-1)) err(1, "stdout");
;
371 }
372 nblank_lines = 0;
373}
374
375/*
376 * Write a line to stdout taking care of space to tab conversion (-h flag)
377 * and character set shifts.
378 */
379void
380flush_line(LINE *l)
381{
382 CHAR *c, *endc;
383 size_t nchars, last_col, this_col;
384
385 last_col = 0;
386 nchars = l->l_line_len;
387
388 if (l->l_needs_sort) {
18
Assuming field 'l_needs_sort' is not equal to 0
19
Taking true branch
389 static CHAR *sorted;
20
'sorted' initialized to a null pointer value
390 static size_t count_size, i, sorted_size;
391 static int *count, save, tot;
392
393 /*
394 * Do an O(n) sort on l->l_line by column being careful to
395 * preserve the order of characters in the same column.
396 */
397 if (l->l_lsize > sorted_size) {
21
Assuming 'sorted_size' is >= field 'l_lsize'
22
Taking false branch
398 sorted_size = l->l_lsize;
399 sorted = xreallocarray(sorted,
400 sorted_size, sizeof(CHAR));
401 }
402 if (l->l_max_col >= count_size) {
23
Assuming 'count_size' is <= field 'l_max_col'
24
Taking true branch
403 count_size = l->l_max_col + 1;
404 count = xreallocarray(count,
405 count_size, sizeof(int));
406 }
407 memset(count, 0, sizeof(*count) * (l->l_max_col + 1));
408 for (i = nchars, c = l->l_line; i-- > 0; c++)
25
Assuming the condition is true
26
Loop condition is true. Entering loop body
27
Assuming the condition is false
28
Loop condition is false. Execution continues on line 415
409 count[c->c_column]++;
410
411 /*
412 * calculate running total (shifted down by 1) to use as
413 * indices into new line.
414 */
415 for (tot = 0, i = 0; i
28.1
'i' is <= field 'l_max_col'
<= l->l_max_col
; i++) {
29
Loop condition is true. Entering loop body
30
Assuming 'i' is > field 'l_max_col'
31
Loop condition is false. Execution continues on line 421
416 save = count[i];
417 count[i] = tot;
418 tot += save;
419 }
420
421 for (i = nchars, c = l->l_line; i-- > 0; c++)
32
Loop condition is true. Entering loop body
33
Loop condition is false. Execution continues on line 423
422 sorted[count[c->c_column]++] = *c;
423 c = sorted;
34
Null pointer value stored to 'c'
424 } else
425 c = l->l_line;
426 while (nchars > 0) {
35
Loop condition is true. Entering loop body
427 this_col = c->c_column;
36
Access to field 'c_column' results in a dereference of a null pointer (loaded from variable 'c')
428 endc = c;
429 do {
430 ++endc;
431 } while (--nchars > 0 && this_col == endc->c_column);
432
433 /* if -b only print last character */
434 if (no_backspaces)
435 c = endc - 1;
436
437 if (this_col > last_col) {
438 size_t nspace = this_col - last_col;
439
440 if (compress_spaces && nspace > 1) {
441 size_t ntabs;
442
443 ntabs = ((last_col % 8) + nspace) / 8;
444 if (ntabs) {
445 nspace -= (ntabs * 8) - (last_col % 8);
446 while (ntabs-- > 0)
447 PUTC('\t')if ((!__isthreaded ? __sputc('\t', (&__sF[1])) : (putc)('\t'
, (&__sF[1]))) == (-1)) err(1, "stdout");
;
448 }
449 }
450 while (nspace-- > 0)
451 PUTC(' ')if ((!__isthreaded ? __sputc(' ', (&__sF[1])) : (putc)(' '
, (&__sF[1]))) == (-1)) err(1, "stdout");
;
452 last_col = this_col;
453 }
454 last_col++;
455
456 for (;;) {
457 if (c->c_set != last_set) {
458 switch (c->c_set) {
459 case CS_NORMAL1:
460 PUTC(SI)if ((!__isthreaded ? __sputc('\017', (&__sF[1])) : (putc)
('\017', (&__sF[1]))) == (-1)) err(1, "stdout");
;
461 break;
462 case CS_ALTERNATE2:
463 PUTC(SO)if ((!__isthreaded ? __sputc('\016', (&__sF[1])) : (putc)
('\016', (&__sF[1]))) == (-1)) err(1, "stdout");
;
464 }
465 last_set = c->c_set;
466 }
467 PUTC(c->c_char)if ((!__isthreaded ? __sputc(c->c_char, (&__sF[1])) : (
putc)(c->c_char, (&__sF[1]))) == (-1)) err(1, "stdout"
);
;
468 if (++c >= endc)
469 break;
470 PUTC('\b')if ((!__isthreaded ? __sputc('\b', (&__sF[1])) : (putc)('\b'
, (&__sF[1]))) == (-1)) err(1, "stdout");
;
471 }
472 }
473}
474
475/*
476 * Increment or decrement a line number, checking for overflow.
477 * Stop one below INT_MAX such that the adjust variable is safe.
478 */
479void
480addto_lineno(int *lno, int offset)
481{
482 if (offset > 0) {
483 if (*lno >= INT_MAX0x7fffffff - offset)
484 errx(1, "too many lines");
485 } else {
486 if (*lno < INT_MIN(-0x7fffffff-1) - offset)
487 errx(1, "too many reverse line feeds");
488 }
489 *lno += offset;
490}
491
492#define NALLOC64 64
493
494static LINE *line_freelist;
495
496LINE *
497alloc_line(void)
498{
499 LINE *l;
500 int i;
501
502 if (!line_freelist) {
503 l = xreallocarray(NULL((void *)0), NALLOC64, sizeof(LINE));
504 line_freelist = l;
505 for (i = 1; i < NALLOC64; i++, l++)
506 l->l_next = l + 1;
507 l->l_next = NULL((void *)0);
508 }
509 l = line_freelist;
510 line_freelist = l->l_next;
511
512 memset(l, 0, sizeof(LINE));
513 return (l);
514}
515
516void
517free_line(LINE *l)
518{
519
520 l->l_next = line_freelist;
521 line_freelist = l;
522}
523
524void *
525xreallocarray(void *p, size_t n, size_t size)
526{
527
528 if (!(p = reallocarray(p, n, size)))
529 err(1, "realloc failed");
530 return (p);
531}
532
533void
534usage(void)
535{
536 (void)fprintf(stderr(&__sF[2]), "usage: col [-bfhx] [-l num]\n");
537 exit(1);
538}
539
540void
541dowarn(int line)
542{
543
544 warnx("warning: can't back up %s",
545 line < 0 ? "past first line" : "-- line already flushed");
546}