Bug Summary

File:src/games/quiz/quiz.c
Warning:line 244, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name quiz.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/quiz/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/quiz/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/quiz/quiz.c
1/* $OpenBSD: quiz.c,v 1.32 2022/08/08 17:54:08 op 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'
43
Returning from 'get_cats'
95
96 if (pledge("stdio", NULL((void *)0)) == -1)
44
Assuming the condition is false
45
Taking false branch
97 err(1, "pledge");
98
99 quiz();
46
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))
24
Assuming the condition is false
25
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) {
26
Assuming the condition is true
27
Loop condition is true. Entering loop body
36
Assuming the condition is false
37
Loop condition is false. Execution continues on line 153
130 if (lp[len - 1] == '\n')
28
Assuming the condition is false
29
Taking false branch
131 lp[--len] = '\0';
132 if (qp->q_text)
30
Assuming field 'q_text' is null
133 qlen = strlen(qp->q_text);
134 if (qlen
30.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))
31
Assuming the condition is false
32
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))
33
Assuming field 'q_text' is not equal to NULL
34
Taking false branch
147 errx(1, "strdup");
148 qp->q_asked = qp->q_answered = FALSE0;
149 qp->q_next = NULL((void *)0);
35
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))
)
38
Assuming '__isthreaded' is not equal to 0
39
'?' condition is false
40
Assuming the condition is false
41
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
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) {
17
Assuming 'catone' is not equal to 0
18
Assuming 'cattwo' is not equal to 0
19
Assuming 'catone' is not equal to 'cattwo'
20
Taking true branch
213 if (!rxp_compile(qp->q_text))
21
Assuming the condition is false
22
Taking false branch
214 errx(1, "%s", rxperr);
215 get_file(rxp_expand());
23
Calling 'get_file'
42
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 size;
228 ssize_t len;
229 u_int guesses, rights, wrongs;
230 int next;
231 char *answer, *t, question[LINE_SZ1024];
232 const char *s;
233
234 size = 0;
235 answer = NULL((void *)0);
236
237 guesses = rights = wrongs = 0;
238 for (;;) {
47
Loop condition is true. Entering loop body
239 if (qsize
47.1
'qsize' is not equal to 0
== 0)
48
Taking false branch
240 break;
241 next = arc4random_uniform(qsize);
242 qp = qlist.q_next;
243 for (i = 0; i < next; i++)
49
Assuming 'i' is < 'next'
50
Loop condition is true. Entering loop body
52
Assuming 'i' is < 'next'
53
Loop condition is true. Entering loop body
244 qp = qp->q_next;
51
Null pointer value stored to 'qp'
54
Access to field 'q_next' results in a dereference of a null pointer (loaded from variable 'qp')
245 while (qp && qp->q_answered)
246 qp = qp->q_next;
247 if (!qp) {
248 qsize = next;
249 continue;
250 }
251 if (tflag && arc4random_uniform(100) > 20) {
252 /* repeat questions in tutorial mode */
253 while (qp && (!qp->q_asked || qp->q_answered))
254 qp = qp->q_next;
255 if (!qp)
256 continue;
257 }
258 s = qp->q_text;
259 for (i = 0; i < catone - 1; i++)
260 s = next_cat(s);
261 if (!rxp_compile(s))
262 errx(1, "%s", rxperr);
263 t = rxp_expand();
264 if (!t || *t == '\0') {
265 qp->q_answered = TRUE1;
266 continue;
267 }
268 (void)strlcpy(question, t, sizeof question);
269 s = qp->q_text;
270 for (i = 0; i < cattwo - 1; i++)
271 s = next_cat(s);
272 if (s == NULL((void *)0))
273 errx(1, "too few fields in data file, line \"%s\"",
274 qp->q_text);
275 if (!rxp_compile(s))
276 errx(1, "%s", rxperr);
277 t = rxp_expand();
278 if (!t || *t == '\0') {
279 qp->q_answered = TRUE1;
280 continue;
281 }
282 qp->q_asked = TRUE1;
283 (void)printf("%s?\n", question);
284 for (;; ++guesses) {
285 if ((len = getline(&answer, &size, stdin(&__sF[0]))) == -1 ||
286 answer[len - 1] != '\n') {
287 score(rights, wrongs, guesses);
288 exit(0);
289 }
290 answer[len - 1] = '\0';
291 downcase(answer);
292 if (rxp_match(answer)) {
293 (void)printf("Right!\n");
294 ++rights;
295 qp->q_answered = TRUE1;
296 break;
297 }
298 if (*answer == '\0') {
299 (void)printf("%s\n", t);
300 ++wrongs;
301 if (!tflag)
302 qp->q_answered = TRUE1;
303 break;
304 }
305 (void)printf("What?\n");
306 }
307 }
308 score(rights, wrongs, guesses);
309 free(answer);
310}
311
312const char *
313next_cat(const char *s)
314{
315 int esc;
316
317 if (s == NULL((void *)0))
318 return (NULL((void *)0));
319 esc = 0;
320 for (;;)
321 switch (*s++) {
322 case '\0':
323 return (NULL((void *)0));
324 case '\\':
325 esc = 1;
326 break;
327 case ':':
328 if (!esc)
329 return (s);
330 default:
331 esc = 0;
332 break;
333 }
334}
335
336void
337score(u_int r, u_int w, u_int g)
338{
339 (void)printf("Rights %d, wrongs %d,", r, w);
340 if (g)
341 (void)printf(" extra guesses %d,", g);
342 (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0);
343}
344
345void
346downcase(char *p)
347{
348 int ch;
349
350 for (; (ch = *p) != '\0'; ++p)
351 if (isascii(ch) && isupper(ch))
352 *p = tolower(ch);
353}
354
355void
356usage(void)
357{
358 (void)fprintf(stderr(&__sF[2]),
359 "usage: %s [-t] [-i file] category1 category2\n", getprogname());
360 exit(1);
361}