Bug Summary

File:src/games/worms/worms.c
Warning:line 313, column 9
Array access results in a null pointer dereference

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 worms.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/games/worms/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/games/worms/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/games/worms/worms.c
1/* $OpenBSD: worms.c,v 1.30 2021/10/23 11:22:49 mestre Exp $ */
2
3/*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 *
34 * @@@ @@@ @@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
35 * @@@ @@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@
36 * @@@ @@@ @@@@ @@@@ @@@@ @@@@ @@@ @@@@
37 * @@@ @@ @@@ @@@ @@@ @@@ @@@ @@@ @@@
38 * @@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@
39 * @@@@ @@@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@
40 * @@@@@@@@@@@@ @@@@ @@@@ @@@ @@@ @@@ @@@
41 * @@@@ @@@@ @@@@@@@@@@@@ @@@ @@@ @@@ @@@
42 * @@ @@ @@@@@@@@@@ @@@ @@@ @@@ @@@
43 *
44 * Eric P. Scott
45 * Caltech High Energy Physics
46 * October, 1980
47 *
48 */
49#include <curses.h>
50#include <err.h>
51#include <signal.h>
52#include <stdlib.h>
53#include <termios.h>
54#include <unistd.h>
55
56static const struct options {
57 int nopts;
58 int opts[3];
59}
60 normal[8] = {
61 { 3, { 7, 0, 1 } },
62 { 3, { 0, 1, 2 } },
63 { 3, { 1, 2, 3 } },
64 { 3, { 2, 3, 4 } },
65 { 3, { 3, 4, 5 } },
66 { 3, { 4, 5, 6 } },
67 { 3, { 5, 6, 7 } },
68 { 3, { 6, 7, 0 } }
69}, upper[8] = {
70 { 1, { 1, 0, 0 } },
71 { 2, { 1, 2, 0 } },
72 { 0, { 0, 0, 0 } },
73 { 0, { 0, 0, 0 } },
74 { 0, { 0, 0, 0 } },
75 { 2, { 4, 5, 0 } },
76 { 1, { 5, 0, 0 } },
77 { 2, { 1, 5, 0 } }
78},
79 left[8] = {
80 { 0, { 0, 0, 0 } },
81 { 0, { 0, 0, 0 } },
82 { 0, { 0, 0, 0 } },
83 { 2, { 2, 3, 0 } },
84 { 1, { 3, 0, 0 } },
85 { 2, { 3, 7, 0 } },
86 { 1, { 7, 0, 0 } },
87 { 2, { 7, 0, 0 } }
88},
89 right[8] = {
90 { 1, { 7, 0, 0 } },
91 { 2, { 3, 7, 0 } },
92 { 1, { 3, 0, 0 } },
93 { 2, { 3, 4, 0 } },
94 { 0, { 0, 0, 0 } },
95 { 0, { 0, 0, 0 } },
96 { 0, { 0, 0, 0 } },
97 { 2, { 6, 7, 0 } }
98},
99 lower[8] = {
100 { 0, { 0, 0, 0 } },
101 { 2, { 0, 1, 0 } },
102 { 1, { 1, 0, 0 } },
103 { 2, { 1, 5, 0 } },
104 { 1, { 5, 0, 0 } },
105 { 2, { 5, 6, 0 } },
106 { 0, { 0, 0, 0 } },
107 { 0, { 0, 0, 0 } }
108},
109 upleft[8] = {
110 { 0, { 0, 0, 0 } },
111 { 0, { 0, 0, 0 } },
112 { 0, { 0, 0, 0 } },
113 { 0, { 0, 0, 0 } },
114 { 0, { 0, 0, 0 } },
115 { 1, { 3, 0, 0 } },
116 { 2, { 1, 3, 0 } },
117 { 1, { 1, 0, 0 } }
118},
119 upright[8] = {
120 { 2, { 3, 5, 0 } },
121 { 1, { 3, 0, 0 } },
122 { 0, { 0, 0, 0 } },
123 { 0, { 0, 0, 0 } },
124 { 0, { 0, 0, 0 } },
125 { 0, { 0, 0, 0 } },
126 { 0, { 0, 0, 0 } },
127 { 1, { 5, 0, 0 } }
128},
129 lowleft[8] = {
130 { 3, { 7, 0, 1 } },
131 { 0, { 0, 0, 0 } },
132 { 0, { 0, 0, 0 } },
133 { 1, { 1, 0, 0 } },
134 { 2, { 1, 7, 0 } },
135 { 1, { 7, 0, 0 } },
136 { 0, { 0, 0, 0 } },
137 { 0, { 0, 0, 0 } }
138},
139 lowright[8] = {
140 { 0, { 0, 0, 0 } },
141 { 1, { 7, 0, 0 } },
142 { 2, { 5, 7, 0 } },
143 { 1, { 5, 0, 0 } },
144 { 0, { 0, 0, 0 } },
145 { 0, { 0, 0, 0 } },
146 { 0, { 0, 0, 0 } },
147 { 0, { 0, 0, 0 } }
148};
149
150static const char flavor[] = {
151 'O', '*', '#', '$', '%', '0', '@', '~'
152};
153static const short xinc[] = {
154 1, 1, 1, 0, -1, -1, -1, 0
155}, yinc[] = {
156 -1, 0, 1, 1, 1, 0, -1, -1
157};
158static struct worm {
159 int orientation, head;
160 short *xpos, *ypos;
161} *worm;
162
163volatile sig_atomic_t sig_caught = 0;
164
165void nomem(void);
166void onsig(int);
167
168int
169main(int argc, char *argv[])
170{
171 int x, y, h, n;
172 struct worm *w;
173 const struct options *op;
174 short *ip;
175 int CO, LI, last, bottom, ch, length, number, trail;
176 short **ref;
177 const char *field, *errstr;
178 struct timespec sleeptime;
179 struct termios term;
180 speed_t speed;
181 time_t delay = 0;
182
183 /* set default delay based on terminal baud rate */
184 if (tcgetattr(STDOUT_FILENO1, &term) == 0 &&
1
Assuming the condition is false
185 (speed = cfgetospeed(&term)) > B96009600)
186 delay = (speed / B96009600) - 1;
187
188 length = 16;
189 number = 3;
190 trail = ' ';
191 field = NULL((void *)0);
192 while ((ch = getopt(argc, argv, "d:fhl:n:t")) != -1)
2
Assuming the condition is true
3
Loop condition is true. Entering loop body
7
Execution continues on line 192
8
Assuming the condition is true
9
Loop condition is true. Entering loop body
13
Execution continues on line 192
14
Assuming the condition is false
15
Loop condition is false. Execution continues on line 226
193 switch(ch) {
4
Control jumps to 'case 110:' at line 209
10
Control jumps to 'case 108:' at line 203
194 case 'd':
195 delay = (time_t)strtonum(optarg, 0, 1000, &errstr);
196 if (errstr)
197 errx(1, "delay (0-1000) is %s: %s", errstr,
198 optarg);
199 break;
200 case 'f':
201 field = "WORM";
202 break;
203 case 'l':
204 length = strtonum(optarg, 2, 1024, &errstr);
205 if (errstr)
11
Assuming 'errstr' is null
12
Taking false branch
206 errx(1, "length (2-1024) is %s: %s", errstr,
207 optarg);
208 break;
209 case 'n':
210 number = strtonum(optarg, 1, 100, &errstr);
211 if (errstr)
5
Assuming 'errstr' is null
6
Taking false branch
212 errx(1, "number of worms (1-100) is %s: %s",
213 errstr, optarg);
214 break;
215 case 't':
216 trail = '.';
217 break;
218 case 'h':
219 default:
220 (void)fprintf(stderr(&__sF[2]), "usage: %s [-ft] [-d delay] "
221 "[-l length] [-n number]\n", getprogname());
222 return 1;
223 }
224
225 /* Convert delay from ms -> ns */
226 sleeptime.tv_sec = 0;
227 sleeptime.tv_nsec = delay * 500000;
228 timespecadd(&sleeptime, &sleeptime, &sleeptime)do { (&sleeptime)->tv_sec = (&sleeptime)->tv_sec
+ (&sleeptime)->tv_sec; (&sleeptime)->tv_nsec =
(&sleeptime)->tv_nsec + (&sleeptime)->tv_nsec;
if ((&sleeptime)->tv_nsec >= 1000000000L) { (&
sleeptime)->tv_sec++; (&sleeptime)->tv_nsec -= 1000000000L
; } } while (0)
;
16
Taking false branch
17
Loop condition is false. Exiting loop
229
230 if (!(worm = calloc(number, sizeof(struct worm))))
18
Assuming 'worm' is non-null
19
Taking false branch
231 nomem();
232 initscr();
233
234 if (pledge("stdio tty", NULL((void *)0)) == -1)
20
Assuming the condition is false
21
Taking false branch
235 err(1, "pledge");
236
237 curs_set(0);
238 CO = COLS;
239 LI = LINES;
240 last = CO - 1;
241 bottom = LI - 1;
242 if (!(ip = reallocarray(NULL((void *)0), LI, CO * sizeof(short))) ||
22
Assuming 'ip' is non-null
25
Taking false branch
243 !(ref = calloc(LI, sizeof(short *)))) {
23
Storing null pointer value
24
Assuming 'ref' is non-null
244 endwin();
245 nomem();
246 }
247 for (n = 0; n < LI; ++n) {
26
Assuming 'n' is >= 'LI'
27
Loop condition is false. Execution continues on line 251
248 ref[n] = ip;
249 ip += CO;
250 }
251 for (ip = ref[0], n = LI * CO; --n >= 0;)
28
Assuming the condition is false
29
Loop condition is false. Execution continues on line 253
252 *ip++ = 0;
253 for (n = number, w = &worm[0]; --n >= 0; w++) {
30
Assuming the condition is true
31
Loop condition is true. Entering loop body
40
Assuming the condition is false
41
Loop condition is false. Execution continues on line 271
254 w->orientation = w->head = 0;
255 if (!(ip = calloc(length, sizeof(short)))) {
32
Assuming 'ip' is non-null
33
Taking false branch
256 endwin();
257 nomem();
258 }
259 w->xpos = ip;
260 for (x = length; --x >= 0;)
34
Assuming the condition is false
35
Loop condition is false. Execution continues on line 262
261 *ip++ = -1;
262 if (!(ip = calloc(length, sizeof(short)))) {
36
Assigning 0
37
Assuming 'ip' is non-null
38
Taking false branch
263 endwin();
264 nomem();
265 }
266 w->ypos = ip;
267 for (y = length; --y >= 0;)
39
Loop condition is false. Execution continues on line 253
268 *ip++ = -1;
269 }
270
271 (void)signal(SIGHUP1, onsig);
272 (void)signal(SIGINT2, onsig);
273 (void)signal(SIGQUIT3, onsig);
274 (void)signal(SIGSTOP17, onsig);
275 (void)signal(SIGTSTP18, onsig);
276 (void)signal(SIGTERM15, onsig);
277
278 if (field
41.1
'field' is null
) {
42
Taking false branch
279 const char *p = field;
280
281 for (y = LI; --y >= 0;) {
282 for (x = CO; --x >= 0;) {
283 addch(*p++)waddch(stdscr,(*p++));
284 if (!*p)
285 p = field;
286 }
287 refresh()wrefresh(stdscr);
288 }
289 }
290 for (;;) {
43
Loop condition is true. Entering loop body
291 refresh()wrefresh(stdscr);
292 if (sig_caught) {
44
Assuming 'sig_caught' is 0
45
Taking false branch
293 endwin();
294 return 0;
295 }
296 nanosleep(&sleeptime, NULL((void *)0));
297 for (n = 0, w = &worm[0]; n < number; n++, w++) {
46
Loop condition is true. Entering loop body
298 if ((x = w->xpos[h = w->head]) < 0) {
47
Taking false branch
299 mvaddch(y = w->ypos[h] = bottom,(wmove((stdscr),((y = w->ypos[h] = bottom)),((x = w->xpos
[h] = 0))) == (-1) ? (-1) : waddch((stdscr),((flavor[n % sizeof
(flavor)]))))
300 x = w->xpos[h] = 0,(wmove((stdscr),((y = w->ypos[h] = bottom)),((x = w->xpos
[h] = 0))) == (-1) ? (-1) : waddch((stdscr),((flavor[n % sizeof
(flavor)]))))
301 flavor[n % sizeof(flavor)])(wmove((stdscr),((y = w->ypos[h] = bottom)),((x = w->xpos
[h] = 0))) == (-1) ? (-1) : waddch((stdscr),((flavor[n % sizeof
(flavor)]))))
;
302 ref[y][x]++;
303 }
304 else
305 y = w->ypos[h];
306 if (++h == length)
48
The value 1 is assigned to 'h'
49
Taking false branch
307 h = 0;
308 if (w->xpos[w->head = h] >= 0) {
50
Taking true branch
309 int x1, y1;
310
311 x1 = w->xpos[h];
312 y1 = w->ypos[h];
51
The value 0 is assigned to 'y1'
313 if (--ref[y1][x1] == 0)
52
Array access results in a null pointer dereference
314 mvaddch(y1, x1, trail)(wmove((stdscr),((y1)),((x1))) == (-1) ? (-1) : waddch((stdscr
),((trail))))
;
315 }
316
317 if (x == 0) {
318 if (y == 0)
319 op = &upleft[w->orientation];
320 else if (y == bottom)
321 op = &lowleft[w->orientation];
322 else
323 op = &left[w->orientation];
324 } else if (x == last) {
325 if (y == 0)
326 op = &upright[w->orientation];
327 else if (y == bottom)
328 op = &lowright[w->orientation];
329 else
330 op = &right[w->orientation];
331 } else {
332 if (y == 0)
333 op = &upper[w->orientation];
334 else if (y == bottom)
335 op = &lower[w->orientation];
336 else
337 op = &normal[w->orientation];
338 }
339
340 switch (op->nopts) {
341 case 0:
342 endwin();
343 return(1);
344 case 1:
345 w->orientation = op->opts[0];
346 break;
347 default:
348 w->orientation =
349 op->opts[arc4random_uniform(op->nopts)];
350 }
351 mvaddch(y += yinc[w->orientation],(wmove((stdscr),((y += yinc[w->orientation])),((x += xinc[
w->orientation]))) == (-1) ? (-1) : waddch((stdscr),((flavor
[n % sizeof(flavor)]))))
352 x += xinc[w->orientation],(wmove((stdscr),((y += yinc[w->orientation])),((x += xinc[
w->orientation]))) == (-1) ? (-1) : waddch((stdscr),((flavor
[n % sizeof(flavor)]))))
353 flavor[n % sizeof(flavor)])(wmove((stdscr),((y += yinc[w->orientation])),((x += xinc[
w->orientation]))) == (-1) ? (-1) : waddch((stdscr),((flavor
[n % sizeof(flavor)]))))
;
354 ref[w->ypos[h] = y][w->xpos[h] = x]++;
355 }
356 }
357}
358
359void
360onsig(int signo)
361{
362 sig_caught = 1;
363}
364
365void
366nomem(void)
367{
368 errx(1, "not enough memory.");
369}