Bug Summary

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

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 true
10
Loop condition is true. Entering loop body
15
Execution continues on line 155
16
Assuming '__isthreaded' is not equal to 0
17
'?' condition is false
18
Assuming the condition is true
19
Loop condition is true. Entering loop body
156 if (!isgraph(ch)) {
11
Assuming the character does not have graphical representation
12
Taking true branch
20
Assuming the character has graphical representation
21
Taking false branch
157 switch (ch) {
13
Control jumps to 'case 10:' at line 191
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
13.1
'cur_line' is > 'max_line'
> max_line)
14
Taking true branch
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) {
22
Taking true branch
219 LINE *lnew;
220
221 /* round up to next line */
222 adjust = !fine && (cur_line & 1);
23
Assuming 'fine' is not equal to 0
223
224 if (cur_line + adjust < this_line) {
24
Taking false branch
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) {
25
Loop condition is true. Entering loop body
28
Loop condition is true. Entering loop body
255 if (l->l_next == NULL((void *)0)) {
26
Assuming field 'l_next' is not equal to NULL
27
Taking false branch
29
Assuming field 'l_next' is equal to NULL
30
Taking true branch
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
30.1
'this_line' is > 'nflushd_lines'
> nflushd_lines &&
32
Taking true branch
264 this_line - nflushd_lines >=
31
Assuming the condition is true
265 max_bufd_lines + BUFFER_MARGIN32) {
266 if (extra_lines
32.1
'extra_lines' is 0
) {
33
Taking false branch
267 flush_lines(extra_lines);
268 extra_lines = 0;
269 }
270 flush_lines(this_line - nflushd_lines -
34
Calling 'flush_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)
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)
303 this_line++;
304 flush_lines(this_line - nflushd_lines + 1);
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) {
35
Assuming the condition is true
36
Loop condition is true. Entering loop body
43
Assuming the condition is true
44
Loop condition is true. Entering loop body
325 l = lines;
45
Null pointer value stored to 'l'
326 lines = l->l_next;
37
Value assigned to 'lines'
46
Access to field 'l_next' results in a dereference of a null pointer (loaded from variable 'l')
327 if (l->l_line) {
38
Assuming field 'l_line' is null
328 flush_blanks();
329 flush_line(l);
330 }
331 if (l->l_line
38.1
Field 'l_line' is null
|| l->l_next
)
39
Assuming pointer value is null
40
Assuming field 'l_next' is null
41
Taking false branch
332 nblank_lines++;
333 if (l->l_line
41.1
Field 'l_line' is null
)
42
Taking false branch
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) {
389 static CHAR *sorted;
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) {
398 sorted_size = l->l_lsize;
399 sorted = xreallocarray(sorted,
400 sorted_size, sizeof(CHAR));
401 }
402 if (l->l_max_col >= count_size) {
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++)
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 <= l->l_max_col; i++) {
416 save = count[i];
417 count[i] = tot;
418 tot += save;
419 }
420
421 for (i = nchars, c = l->l_line; i-- > 0; c++)
422 sorted[count[c->c_column]++] = *c;
423 c = sorted;
424 } else
425 c = l->l_line;
426 while (nchars > 0) {
427 this_col = c->c_column;
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}