Bug Summary

File:src/usr.bin/mandoc/dbm.c
Warning:line 447, column 7
Dereference of null pointer (loaded from variable 'pp')

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 dbm.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/usr.bin/mandoc/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 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/mandoc/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/usr.bin/mandoc/dbm.c
1/* $OpenBSD: dbm.c,v 1.5 2019/07/01 22:43:03 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Map-based version of the mandoc database, for read-only access.
18 * The interface is defined in "dbm.h".
19 */
20#include <assert.h>
21#include <endian.h>
22#include <err.h>
23#include <errno(*__errno()).h>
24#include <regex.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include "mansearch.h"
31#include "dbm_map.h"
32#include "dbm.h"
33
34struct macro {
35 int32_t value;
36 int32_t pages;
37};
38
39struct page {
40 int32_t name;
41 int32_t sect;
42 int32_t arch;
43 int32_t desc;
44 int32_t file;
45};
46
47enum iter {
48 ITER_NONE = 0,
49 ITER_NAME,
50 ITER_SECT,
51 ITER_ARCH,
52 ITER_DESC,
53 ITER_MACRO
54};
55
56static struct macro *macros[MACRO_MAX36];
57static int32_t nvals[MACRO_MAX36];
58static struct page *pages;
59static int32_t npages;
60static enum iter iteration;
61
62static struct dbm_res page_bytitle(enum iter, const struct dbm_match *);
63static struct dbm_res page_byarch(const struct dbm_match *);
64static struct dbm_res page_bymacro(int32_t, const struct dbm_match *);
65static char *macro_bypage(int32_t, int32_t);
66
67
68/*** top level functions **********************************************/
69
70/*
71 * Open a disk-based mandoc database for read-only access.
72 * Map the pages and macros[] arrays.
73 * Return 0 on success. Return -1 and set errno on failure.
74 */
75int
76dbm_open(const char *fname)
77{
78 const int32_t *mp, *ep;
79 int32_t im;
80
81 if (dbm_map(fname) == -1)
82 return -1;
83
84 if ((npages = be32toh(*dbm_getint(4))(__uint32_t)(__builtin_constant_p(*dbm_getint(4)) ? (__uint32_t
)(((__uint32_t)(*dbm_getint(4)) & 0xff) << 24 | ((__uint32_t
)(*dbm_getint(4)) & 0xff00) << 8 | ((__uint32_t)(*dbm_getint
(4)) & 0xff0000) >> 8 | ((__uint32_t)(*dbm_getint(4
)) & 0xff000000) >> 24) : __swap32md(*dbm_getint(4)
))
) < 0) {
85 warnx("dbm_open(%s): Invalid number of pages: %d",
86 fname, npages);
87 goto fail;
88 }
89 pages = (struct page *)dbm_getint(5);
90
91 if ((mp = dbm_get(*dbm_getint(2))) == NULL((void *)0)) {
92 warnx("dbm_open(%s): Invalid offset of macros array", fname);
93 goto fail;
94 }
95 if (be32toh(*mp)(__uint32_t)(__builtin_constant_p(*mp) ? (__uint32_t)(((__uint32_t
)(*mp) & 0xff) << 24 | ((__uint32_t)(*mp) & 0xff00
) << 8 | ((__uint32_t)(*mp) & 0xff0000) >> 8 |
((__uint32_t)(*mp) & 0xff000000) >> 24) : __swap32md
(*mp))
!= MACRO_MAX36) {
96 warnx("dbm_open(%s): Invalid number of macros: %d",
97 fname, be32toh(*mp)(__uint32_t)(__builtin_constant_p(*mp) ? (__uint32_t)(((__uint32_t
)(*mp) & 0xff) << 24 | ((__uint32_t)(*mp) & 0xff00
) << 8 | ((__uint32_t)(*mp) & 0xff0000) >> 8 |
((__uint32_t)(*mp) & 0xff000000) >> 24) : __swap32md
(*mp))
);
98 goto fail;
99 }
100 for (im = 0; im < MACRO_MAX36; im++) {
101 if ((ep = dbm_get(*++mp)) == NULL((void *)0)) {
102 warnx("dbm_open(%s): Invalid offset of macro %d",
103 fname, im);
104 goto fail;
105 }
106 nvals[im] = be32toh(*ep)(__uint32_t)(__builtin_constant_p(*ep) ? (__uint32_t)(((__uint32_t
)(*ep) & 0xff) << 24 | ((__uint32_t)(*ep) & 0xff00
) << 8 | ((__uint32_t)(*ep) & 0xff0000) >> 8 |
((__uint32_t)(*ep) & 0xff000000) >> 24) : __swap32md
(*ep))
;
107 macros[im] = (struct macro *)++ep;
108 }
109 return 0;
110
111fail:
112 dbm_unmap();
113 errno(*__errno()) = EFTYPE79;
114 return -1;
115}
116
117void
118dbm_close(void)
119{
120 dbm_unmap();
121}
122
123
124/*** functions for handling pages *************************************/
125
126int32_t
127dbm_page_count(void)
128{
129 return npages;
130}
131
132/*
133 * Give the caller pointers to the data for one manual page.
134 */
135struct dbm_page *
136dbm_page_get(int32_t ip)
137{
138 static struct dbm_page res;
139
140 assert(ip >= 0)((ip >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 140, __func__, "ip >= 0"))
;
141 assert(ip < npages)((ip < npages) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 141, __func__, "ip < npages"))
;
142 res.name = dbm_get(pages[ip].name);
143 if (res.name == NULL((void *)0))
144 res.name = "(NULL)\0";
145 res.sect = dbm_get(pages[ip].sect);
146 if (res.sect == NULL((void *)0))
147 res.sect = "(NULL)\0";
148 res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL((void *)0);
149 res.desc = dbm_get(pages[ip].desc);
150 if (res.desc == NULL((void *)0))
151 res.desc = "(NULL)";
152 res.file = dbm_get(pages[ip].file);
153 if (res.file == NULL((void *)0))
154 res.file = " (NULL)\0";
155 res.addr = dbm_addr(pages + ip);
156 return &res;
157}
158
159/*
160 * Functions to start filtered iterations over manual pages.
161 */
162void
163dbm_page_byname(const struct dbm_match *match)
164{
165 assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 165, __func__, "match != NULL"))
;
166 page_bytitle(ITER_NAME, match);
167}
168
169void
170dbm_page_bysect(const struct dbm_match *match)
171{
172 assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 172, __func__, "match != NULL"))
;
173 page_bytitle(ITER_SECT, match);
174}
175
176void
177dbm_page_byarch(const struct dbm_match *match)
178{
179 assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 179, __func__, "match != NULL"))
;
180 page_byarch(match);
181}
182
183void
184dbm_page_bydesc(const struct dbm_match *match)
185{
186 assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 186, __func__, "match != NULL"))
;
187 page_bytitle(ITER_DESC, match);
188}
189
190void
191dbm_page_bymacro(int32_t im, const struct dbm_match *match)
192{
193 assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 193, __func__, "im >= 0"))
;
194 assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 194, __func__, "im < MACRO_MAX"))
;
195 assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 195, __func__, "match != NULL"))
;
196 page_bymacro(im, match);
197}
198
199/*
200 * Return the number of the next manual page in the current iteration.
201 */
202struct dbm_res
203dbm_page_next(void)
204{
205 struct dbm_res res = {-1, 0};
206
207 switch(iteration) {
208 case ITER_NONE:
209 return res;
210 case ITER_ARCH:
211 return page_byarch(NULL((void *)0));
212 case ITER_MACRO:
213 return page_bymacro(0, NULL((void *)0));
214 default:
215 return page_bytitle(iteration, NULL((void *)0));
216 }
217}
218
219/*
220 * Functions implementing the iteration over manual pages.
221 */
222static struct dbm_res
223page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
224{
225 static const struct dbm_match *match;
226 static const char *cp;
227 static int32_t ip;
228 struct dbm_res res = {-1, 0};
229
230 assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||((arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter ==
ITER_SECT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 231, __func__, "arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter == ITER_SECT"
))
231 arg_iter == ITER_SECT)((arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter ==
ITER_SECT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 231, __func__, "arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter == ITER_SECT"
))
;
232
233 /* Initialize for a new iteration. */
234
235 if (arg_match != NULL((void *)0)) {
236 iteration = arg_iter;
237 match = arg_match;
238 switch (iteration) {
239 case ITER_NAME:
240 cp = dbm_get(pages[0].name);
241 break;
242 case ITER_SECT:
243 cp = dbm_get(pages[0].sect);
244 break;
245 case ITER_DESC:
246 cp = dbm_get(pages[0].desc);
247 break;
248 default:
249 abort();
250 }
251 if (cp == NULL((void *)0)) {
252 iteration = ITER_NONE;
253 match = NULL((void *)0);
254 cp = NULL((void *)0);
255 ip = npages;
256 } else
257 ip = 0;
258 return res;
259 }
260
261 /* Search for a name. */
262
263 while (ip < npages) {
264 if (iteration == ITER_NAME)
265 cp++;
266 if (dbm_match(match, cp))
267 break;
268 cp = strchr(cp, '\0') + 1;
269 if (iteration == ITER_DESC)
270 ip++;
271 else if (*cp == '\0') {
272 cp++;
273 ip++;
274 }
275 }
276
277 /* Reached the end without a match. */
278
279 if (ip == npages) {
280 iteration = ITER_NONE;
281 match = NULL((void *)0);
282 cp = NULL((void *)0);
283 return res;
284 }
285
286 /* Found a match; save the quality for later retrieval. */
287
288 res.page = ip;
289 res.bits = iteration == ITER_NAME ? cp[-1] : 0;
290
291 /* Skip the remaining names of this page. */
292
293 if (++ip < npages) {
294 do {
295 cp++;
296 } while (cp[-1] != '\0' ||
297 (iteration != ITER_DESC && cp[-2] != '\0'));
298 }
299 return res;
300}
301
302static struct dbm_res
303page_byarch(const struct dbm_match *arg_match)
304{
305 static const struct dbm_match *match;
306 struct dbm_res res = {-1, 0};
307 static int32_t ip;
308 const char *cp;
309
310 /* Initialize for a new iteration. */
311
312 if (arg_match != NULL((void *)0)) {
313 iteration = ITER_ARCH;
314 match = arg_match;
315 ip = 0;
316 return res;
317 }
318
319 /* Search for an architecture. */
320
321 for ( ; ip < npages; ip++)
322 if (pages[ip].arch)
323 for (cp = dbm_get(pages[ip].arch);
324 *cp != '\0';
325 cp = strchr(cp, '\0') + 1)
326 if (dbm_match(match, cp)) {
327 res.page = ip++;
328 return res;
329 }
330
331 /* Reached the end without a match. */
332
333 iteration = ITER_NONE;
334 match = NULL((void *)0);
335 return res;
336}
337
338static struct dbm_res
339page_bymacro(int32_t arg_im, const struct dbm_match *arg_match)
340{
341 static const struct dbm_match *match;
342 static const int32_t *pp;
343 static const char *cp;
344 static int32_t im, iv;
345 struct dbm_res res = {-1, 0};
346
347 assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 347, __func__, "im >= 0"))
;
348 assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 348, __func__, "im < MACRO_MAX"))
;
349
350 /* Initialize for a new iteration. */
351
352 if (arg_match != NULL((void *)0)) {
353 iteration = ITER_MACRO;
354 match = arg_match;
355 im = arg_im;
356 cp = nvals[im] ? dbm_get(macros[im]->value) : NULL((void *)0);
357 pp = NULL((void *)0);
358 iv = -1;
359 return res;
360 }
361 if (iteration != ITER_MACRO)
362 return res;
363
364 /* Find the next matching macro value. */
365
366 while (pp == NULL((void *)0) || *pp == 0) {
367 if (++iv == nvals[im]) {
368 iteration = ITER_NONE;
369 return res;
370 }
371 if (iv)
372 cp = strchr(cp, '\0') + 1;
373 if (dbm_match(match, cp))
374 pp = dbm_get(macros[im][iv].pages);
375 }
376
377 /* Found a matching page. */
378
379 res.page = (struct page *)dbm_get(*pp++) - pages;
380 return res;
381}
382
383
384/*** functions for handling macros ************************************/
385
386int32_t
387dbm_macro_count(int32_t im)
388{
389 assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 389, __func__, "im >= 0"))
;
390 assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 390, __func__, "im < MACRO_MAX"))
;
391 return nvals[im];
392}
393
394struct dbm_macro *
395dbm_macro_get(int32_t im, int32_t iv)
396{
397 static struct dbm_macro macro;
398
399 assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 399, __func__, "im >= 0"))
;
400 assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 400, __func__, "im < MACRO_MAX"))
;
401 assert(iv >= 0)((iv >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 401, __func__, "iv >= 0"))
;
402 assert(iv < nvals[im])((iv < nvals[im]) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 402, __func__, "iv < nvals[im]"))
;
403 macro.value = dbm_get(macros[im][iv].value);
404 macro.pp = dbm_get(macros[im][iv].pages);
405 return &macro;
406}
407
408/*
409 * Filtered iteration over macro entries.
410 */
411void
412dbm_macro_bypage(int32_t im, int32_t ip)
413{
414 assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 414, __func__, "im >= 0"))
;
415 assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 415, __func__, "im < MACRO_MAX"))
;
416 assert(ip != 0)((ip != 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c"
, 416, __func__, "ip != 0"))
;
417 macro_bypage(im, ip);
418}
419
420char *
421dbm_macro_next(void)
422{
423 return macro_bypage(MACRO_MAX36, 0);
1
Calling 'macro_bypage'
424}
425
426static char *
427macro_bypage(int32_t arg_im, int32_t arg_ip)
428{
429 static const int32_t *pp;
2
'pp' initialized to a null pointer value
430 static int32_t im, ip, iv;
431
432 /* Initialize for a new iteration. */
433
434 if (arg_im
2.1
'arg_im' is >= MACRO_MAX
< MACRO_MAX36 && arg_ip != 0) {
435 im = arg_im;
436 ip = arg_ip;
437 pp = dbm_get(macros[im]->pages);
438 iv = 0;
439 return NULL((void *)0);
440 }
441 if (im
2.2
'im' is < MACRO_MAX
>= MACRO_MAX36)
3
Taking false branch
442 return NULL((void *)0);
443
444 /* Search for the next value. */
445
446 while (iv < nvals[im]) {
4
Assuming the condition is true
5
Loop condition is true. Entering loop body
447 if (*pp == ip)
6
Dereference of null pointer (loaded from variable 'pp')
448 break;
449 if (*pp == 0)
450 iv++;
451 pp++;
452 }
453
454 /* Reached the end without a match. */
455
456 if (iv == nvals[im]) {
457 im = MACRO_MAX36;
458 ip = 0;
459 pp = NULL((void *)0);
460 return NULL((void *)0);
461 }
462
463 /* Found a match; skip the remaining pages of this entry. */
464
465 if (++iv < nvals[im])
466 while (*pp++ != 0)
467 continue;
468
469 return dbm_get(macros[im][iv - 1].value);
470}