Bug Summary

File:src/games/quiz/quiz.c
Warning:line 240, column 9
Access to field 'q_next' results in a dereference of a null pointer (loaded from variable 'qp')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name quiz.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -fno-rounding-math -mconstructor-aliases -munwind-tables -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/quiz/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/games/quiz/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -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/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/games/quiz/quiz.c
1/* $OpenBSD: quiz.c,v 1.31 2021/03/11 21:18:25 naddy Exp $ */
2/* $NetBSD: quiz.c,v 1.9 1995/04/22 10:16:58 cgd Exp $ */
3
4/*-
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jim R. Oldroyd at The Instruction Set and Keith Gabryelski at
10 * Commodore Business Machines.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <ctype.h>
38#include <err.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "pathnames.h"
45#include "quiz.h"
46
47static QE qlist;
48static int catone, cattwo, tflag;
49static u_int qsize;
50
51void downcase(char *);
52void get_cats(char *, char *);
53void get_file(const char *);
54const char *next_cat(const char *);
55void quiz(void);
56void score(u_int, u_int, u_int);
57void show_index(void);
58__dead__attribute__((__noreturn__)) void usage(void);
59
60int
61main(int argc, char *argv[])
62{
63 int ch;
64 const char *indexfile;
65
66 if (pledge("stdio rpath proc exec", NULL((void *)0)) == -1)
1
Assuming the condition is false
2
Taking false branch
67 err(1, "pledge");
68
69 indexfile = _PATH_QUIZIDX"/usr/share/games/quiz.db/index";
70 while ((ch = getopt(argc, argv, "hi:t")) != -1)
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 82
71 switch(ch) {
72 case 'i':
73 indexfile = optarg;
74 break;
75 case 't':
76 tflag = 1;
77 break;
78 case 'h':
79 default:
80 usage();
81 }
82 argc -= optind;
83 argv += optind;
84
85 switch(argc) {
5
Control jumps to 'case 2:' at line 90
86 case 0:
87 get_file(indexfile);
88 show_index();
89 break;
90 case 2:
91 if (pledge("stdio rpath", NULL((void *)0)) == -1)
6
Assuming the condition is false
7
Taking false branch
92 err(1, "pledge");
93 get_file(indexfile);
94 get_cats(argv[0], argv[1]);
8
Calling 'get_cats'
44
Returning from 'get_cats'
95
96 if (pledge("stdio", NULL((void *)0)) == -1)
45
Assuming the condition is false
46
Taking false branch
97 err(1, "pledge");
98
99 quiz();
47
Calling 'quiz'
100 break;
101 default:
102 usage();
103 }
104 return 0;
105}
106
107void
108get_file(const char *file)
109{
110 FILE *fp;
111 QE *qp;
112 ssize_t len;
113 size_t qlen, size;
114 char *lp;
115
116 if ((fp = fopen(file, "r")) == NULL((void *)0))
25
Assuming the condition is false
26
Taking false branch
117 err(1, "%s", file);
118
119 /*
120 * XXX
121 * Should really free up space from any earlier read list
122 * but there are no reverse pointers to do so with.
123 */
124 qp = &qlist;
125 qsize = 0;
126 qlen = 0;
127 lp = NULL((void *)0);
128 size = 0;
129 while ((len = getline(&lp, &size, fp)) != -1) {
27
Assuming the condition is true
28
Loop condition is true. Entering loop body
38
Assuming the condition is false
39
Loop condition is false. Execution continues on line 153
130 if (lp[len - 1] == '\n')
29
Assuming the condition is false
30
Taking false branch
131 lp[--len] = '\0';
132 if (qp->q_text)
31
Assuming field 'q_text' is null
32
Taking false branch
133 qlen = strlen(qp->q_text);
134 if (qlen
32.1
'qlen' is <= 0
> 0 && qp->q_text[qlen - 1] == '\\') {
135 qp->q_text[--qlen] = '\0';
136 qlen += len;
137 qp->q_text = realloc(qp->q_text, qlen + 1);
138 if (qp->q_text == NULL((void *)0))
139 errx(1, "realloc");
140 strlcat(qp->q_text, lp, qlen + 1);
141 } else {
142 if ((qp->q_next = malloc(sizeof(QE))) == NULL((void *)0))
33
Assuming the condition is false
34
Taking false branch
143 errx(1, "malloc");
144 qp = qp->q_next;
145 qp->q_text = strdup(lp);
146 if (qp->q_text == NULL((void *)0))
35
Assuming field 'q_text' is not equal to NULL
36
Taking false branch
147 errx(1, "strdup");
148 qp->q_asked = qp->q_answered = FALSE0;
149 qp->q_next = NULL((void *)0);
37
Null pointer value stored to field 'q_next'
150 ++qsize;
151 }
152 }
153 free(lp);
154 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
)
40
'?' condition is false
41
Assuming the condition is false
42
Taking false branch
155 err(1, "getline");
156 (void)fclose(fp);
157}
158
159void
160show_index(void)
161{
162 QE *qp;
163 const char *p, *s;
164 FILE *pf;
165 const char *pager;
166
167 if (!isatty(1))
168 pager = "/bin/cat";
169 else if (!(pager = getenv("PAGER")) || (*pager == 0))
170 pager = _PATH_PAGER"/usr/bin/more";
171 if ((pf = popen(pager, "w")) == NULL((void *)0))
172 err(1, "%s", pager);
173 (void)fprintf(pf, "Subjects:\n\n");
174 for (qp = qlist.q_next; qp; qp = qp->q_next) {
175 for (s = next_cat(qp->q_text); s; s = next_cat(s)) {
176 if (!rxp_compile(s))
177 errx(1, "%s", rxperr);
178 if ((p = rxp_expand()))
179 (void)fprintf(pf, "%s ", p);
180 }
181 (void)fprintf(pf, "\n");
182 }
183 (void)fprintf(pf, "\n%s\n%s\n%s\n",
184"For example, \"quiz victim killer\" prints a victim's name and you reply",
185"with the killer, and \"quiz killer victim\" works the other way around.",
186"Type an empty line to get the correct answer.");
187 (void)pclose(pf);
188}
189
190void
191get_cats(char *cat1, char *cat2)
192{
193 QE *qp;
194 int i;
195 const char *s;
196
197 downcase(cat1);
198 downcase(cat2);
199 for (qp = qlist.q_next; qp; qp = qp->q_next) {
9
Loop condition is true. Entering loop body
200 s = next_cat(qp->q_text);
201 catone = cattwo = i = 0;
202 while (s) {
10
Loop condition is true. Entering loop body
17
Loop condition is false. Execution continues on line 212
203 if (!rxp_compile(s))
11
Assuming the condition is false
12
Taking false branch
204 errx(1, "%s", rxperr);
205 i++;
206 if (rxp_match(cat1))
13
Assuming the condition is false
14
Taking false branch
207 catone = i;
208 if (rxp_match(cat2))
15
Assuming the condition is false
16
Taking false branch
209 cattwo = i;
210 s = next_cat(s);
211 }
212 if (catone && cattwo && catone != cattwo) {
18
Assuming 'catone' is not equal to 0
19
Assuming 'cattwo' is not equal to 0
20
Assuming 'catone' is not equal to 'cattwo'
21
Taking true branch
213 if (!rxp_compile(qp->q_text))
22
Assuming the condition is false
23
Taking false branch
214 errx(1, "%s", rxperr);
215 get_file(rxp_expand());
24
Calling 'get_file'
43
Returning from 'get_file'
216 return;
217 }
218 }
219 errx(1, "invalid categories");
220}
221
222void
223quiz(void)
224{
225 QE *qp;
226 int i;
227 size_t len;
228 u_int guesses, rights, wrongs;
229 int next;
230 char *answer, *t, question[LINE_SZ1024];
231 const char *s;
232
233 guesses = rights = wrongs = 0;
234 for (;;) {
48
Loop condition is true. Entering loop body
235 if (qsize
48.1
'qsize' is not equal to 0
== 0)
49
Taking false branch
236 break;
237 next = arc4random_uniform(qsize);
238 qp = qlist.q_next;
239 for (i = 0; i < next; i++)
50
Assuming 'i' is < 'next'
51
Loop condition is true. Entering loop body
53
Assuming 'i' is < 'next'
54
Loop condition is true. Entering loop body
240 qp = qp->q_next;
52
Null pointer value stored to 'qp'
55
Access to field 'q_next' results in a dereference of a null pointer (loaded from variable 'qp')
241 while (qp && qp->q_answered)
242 qp = qp->q_next;
243 if (!qp) {
244 qsize = next;
245 continue;
246 }
247 if (tflag && arc4random_uniform(100) > 20) {
248 /* repeat questions in tutorial mode */
249 while (qp && (!qp->q_asked || qp->q_answered))
250 qp = qp->q_next;
251 if (!qp)
252 continue;
253 }
254 s = qp->q_text;
255 for (i = 0; i < catone - 1; i++)
256 s = next_cat(s);
257 if (!rxp_compile(s))
258 errx(1, "%s", rxperr);
259 t = rxp_expand();
260 if (!t || *t == '\0') {
261 qp->q_answered = TRUE1;
262 continue;
263 }
264 (void)strlcpy(question, t, sizeof question);
265 s = qp->q_text;
266 for (i = 0; i < cattwo - 1; i++)
267 s = next_cat(s);
268 if (s == NULL((void *)0))
269 errx(1, "too few fields in data file, line \"%s\"",
270 qp->q_text);
271 if (!rxp_compile(s))
272 errx(1, "%s", rxperr);
273 t = rxp_expand();
274 if (!t || *t == '\0') {
275 qp->q_answered = TRUE1;
276 continue;
277 }
278 qp->q_asked = TRUE1;
279 (void)printf("%s?\n", question);
280 for (;; ++guesses) {
281 if ((answer = fgetln(stdin(&__sF[0]), &len)) == NULL((void *)0) ||
282 answer[len - 1] != '\n') {
283 score(rights, wrongs, guesses);
284 exit(0);
285 }
286 answer[len - 1] = '\0';
287 downcase(answer);
288 if (rxp_match(answer)) {
289 (void)printf("Right!\n");
290 ++rights;
291 qp->q_answered = TRUE1;
292 break;
293 }
294 if (*answer == '\0') {
295 (void)printf("%s\n", t);
296 ++wrongs;
297 if (!tflag)
298 qp->q_answered = TRUE1;
299 break;
300 }
301 (void)printf("What?\n");
302 }
303 }
304 score(rights, wrongs, guesses);
305}
306
307const char *
308next_cat(const char *s)
309{
310 int esc;
311
312 if (s == NULL((void *)0))
313 return (NULL((void *)0));
314 esc = 0;
315 for (;;)
316 switch (*s++) {
317 case '\0':
318 return (NULL((void *)0));
319 case '\\':
320 esc = 1;
321 break;
322 case ':':
323 if (!esc)
324 return (s);
325 default:
326 esc = 0;
327 break;
328 }
329}
330
331void
332score(u_int r, u_int w, u_int g)
333{
334 (void)printf("Rights %d, wrongs %d,", r, w);
335 if (g)
336 (void)printf(" extra guesses %d,", g);
337 (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0);
338}
339
340void
341downcase(char *p)
342{
343 int ch;
344
345 for (; (ch = *p) != '\0'; ++p)
346 if (isascii(ch) && isupper(ch))
347 *p = tolower(ch);
348}
349
350void
351usage(void)
352{
353 (void)fprintf(stderr(&__sF[2]),
354 "usage: %s [-t] [-i file] category1 category2\n", getprogname());
355 exit(1);
356}