Bug Summary

File:src/usr.bin/less/less/../tags.c
Warning:line 78, column 3
Use of memory after it is freed

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 tags.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/less/less/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/less/less/.. -D SYSDIR="/etc" -D HELPDIR="/usr/share/misc" -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/less/less/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/less/less/../tags.c
1/*
2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5 *
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
8 *
9 * For more information, see the README file.
10 */
11
12#include "less.h"
13
14#define WHITESP(c)((c) == ' ' || (c) == '\t') ((c) == ' ' || (c) == '\t')
15
16char *tags = "tags";
17
18static int total;
19static int curseq;
20
21extern int linenums;
22
23enum tag_result {
24 TAG_FOUND,
25 TAG_NOFILE,
26 TAG_NOTAG,
27 TAG_NOTYPE,
28 TAG_INTR
29};
30
31static enum tag_result findctag(char *);
32static char *nextctag(void);
33static char *prevctag(void);
34static off_t ctagsearch(void);
35
36/*
37 * The list of tags generated by the last findctag() call.
38 */
39struct taglist {
40 struct tag *tl_first;
41 struct tag *tl_last;
42};
43#define TAG_END((struct tag *)&taglist) ((struct tag *)&taglist)
44static struct taglist taglist = { TAG_END((struct tag *)&taglist), TAG_END((struct tag *)&taglist) };
45struct tag {
46 struct tag *next, *prev; /* List links */
47 char *tag_file; /* Source file containing the tag */
48 off_t tag_linenum; /* Appropriate line number in source file */
49 char *tag_pattern; /* Pattern used to find the tag */
50 int tag_endline; /* True if the pattern includes '$' */
51};
52static struct tag *curtag;
53
54#define TAG_INS(tp)(tp)->next = ((struct tag *)&taglist); (tp)->prev =
taglist.tl_last; taglist.tl_last->next = (tp); taglist.tl_last
= (tp);
\
55 (tp)->next = TAG_END((struct tag *)&taglist); \
56 (tp)->prev = taglist.tl_last; \
57 taglist.tl_last->next = (tp); \
58 taglist.tl_last = (tp);
59
60#define TAG_RM(tp)(tp)->next->prev = (tp)->prev; (tp)->prev->next
= (tp)->next;
\
61 (tp)->next->prev = (tp)->prev; \
62 (tp)->prev->next = (tp)->next;
63
64/*
65 * Delete tag structures.
66 */
67void
68cleantags(void)
69{
70 struct tag *tp;
71
72 /*
73 * Delete any existing tag list.
74 * {{ Ideally, we wouldn't do this until after we know that we
75 * can load some other tag information. }}
76 */
77 while ((tp = taglist.tl_first) != TAG_END((struct tag *)&taglist)) {
5
Loop condition is true. Entering loop body
7
Loop condition is true. Entering loop body
78 TAG_RM(tp)(tp)->next->prev = (tp)->prev; (tp)->prev->next
= (tp)->next;
;
8
Use of memory after it is freed
79 free(tp->tag_file);
80 free(tp->tag_pattern);
81 free(tp);
6
Memory is released
82 }
83 curtag = NULL((void *)0);
84 total = curseq = 0;
85}
86
87/*
88 * Create a new tag entry.
89 */
90static struct tag *
91maketagent(char *file, off_t linenum, char *pattern, int endline)
92{
93 struct tag *tp;
94
95 tp = ecalloc(sizeof (struct tag), 1);
96 tp->tag_file = estrdup(file);
97 tp->tag_linenum = linenum;
98 tp->tag_endline = endline;
99 if (pattern == NULL((void *)0))
100 tp->tag_pattern = NULL((void *)0);
101 else
102 tp->tag_pattern = estrdup(pattern);
103 return (tp);
104}
105
106/*
107 * Find tags in tag file.
108 */
109void
110findtag(char *tag)
111{
112 enum tag_result result;
113
114 result = findctag(tag);
1
Calling 'findctag'
115 switch (result) {
116 case TAG_FOUND:
117 case TAG_INTR:
118 break;
119 case TAG_NOFILE:
120 error("No tags file", NULL((void *)0));
121 break;
122 case TAG_NOTAG:
123 error("No such tag in tags file", NULL((void *)0));
124 break;
125 case TAG_NOTYPE:
126 error("unknown tag type", NULL((void *)0));
127 break;
128 }
129}
130
131/*
132 * Search for a tag.
133 */
134off_t
135tagsearch(void)
136{
137 if (curtag == NULL((void *)0))
138 return (-1); /* No tags loaded! */
139 if (curtag->tag_linenum != 0)
140 return (find_pos(curtag->tag_linenum));
141 return (ctagsearch());
142}
143
144/*
145 * Go to the next tag.
146 */
147char *
148nexttag(int n)
149{
150 char *tagfile = NULL((void *)0);
151
152 while (n-- > 0)
153 tagfile = nextctag();
154 return (tagfile);
155}
156
157/*
158 * Go to the previous tag.
159 */
160char *
161prevtag(int n)
162{
163 char *tagfile = NULL((void *)0);
164
165 while (n-- > 0)
166 tagfile = prevctag();
167 return (tagfile);
168}
169
170/*
171 * Return the total number of tags.
172 */
173int
174ntags(void)
175{
176 return (total);
177}
178
179/*
180 * Return the sequence number of current tag.
181 */
182int
183curr_tag(void)
184{
185 return (curseq);
186}
187
188/*
189 * Find tags in the "tags" file.
190 * Sets curtag to the first tag entry.
191 */
192static enum tag_result
193findctag(char *tag)
194{
195 char *p;
196 FILE *f;
197 int taglen;
198 off_t taglinenum;
199 char *tagfile;
200 char *tagpattern;
201 int tagendline;
202 int search_char;
203 int err;
204 char tline[TAGLINE_SIZE1024];
205 struct tag *tp;
206
207 p = shell_unquote(tags);
208 f = fopen(p, "r");
209 free(p);
210 if (f == NULL((void *)0))
2
Assuming 'f' is not equal to NULL
3
Taking false branch
211 return (TAG_NOFILE);
212
213 cleantags();
4
Calling 'cleantags'
214 total = 0;
215 taglen = strlen(tag);
216
217 /*
218 * Search the tags file for the desired tag.
219 */
220 while (fgets(tline, sizeof (tline), f) != NULL((void *)0)) {
221 if (tline[0] == '!')
222 /* Skip header of extended format. */
223 continue;
224 if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])((tline[taglen]) == ' ' || (tline[taglen]) == '\t'))
225 continue;
226
227 /*
228 * Found it.
229 * The line contains the tag, the filename and the
230 * location in the file, separated by white space.
231 * The location is either a decimal line number,
232 * or a search pattern surrounded by a pair of delimiters.
233 * Parse the line and extract these parts.
234 */
235 tagpattern = NULL((void *)0);
236
237 /*
238 * Skip over the whitespace after the tag name.
239 */
240 p = skipsp(tline+taglen);
241 if (*p == '\0')
242 /* File name is missing! */
243 continue;
244
245 /*
246 * Save the file name.
247 * Skip over the whitespace after the file name.
248 */
249 tagfile = p;
250 while (!WHITESP(*p)((*p) == ' ' || (*p) == '\t') && *p != '\0')
251 p++;
252 *p++ = '\0';
253 p = skipsp(p);
254 if (*p == '\0')
255 /* Pattern is missing! */
256 continue;
257
258 /*
259 * First see if it is a line number.
260 */
261 tagendline = 0;
262 taglinenum = getnum(&p, 0, &err);
263 if (err) {
264 /*
265 * No, it must be a pattern.
266 * Delete the initial "^" (if present) and
267 * the final "$" from the pattern.
268 * Delete any backslash in the pattern.
269 */
270 taglinenum = 0;
271 search_char = *p++;
272 if (*p == '^')
273 p++;
274 tagpattern = p;
275 while (*p != search_char && *p != '\0') {
276 if (*p == '\\')
277 p++;
278 p++;
279 }
280 tagendline = (p[-1] == '$');
281 if (tagendline)
282 p--;
283 *p = '\0';
284 }
285 tp = maketagent(tagfile, taglinenum, tagpattern, tagendline);
286 TAG_INS(tp)(tp)->next = ((struct tag *)&taglist); (tp)->prev =
taglist.tl_last; taglist.tl_last->next = (tp); taglist.tl_last
= (tp);
;
287 total++;
288 }
289 fclose(f);
290 if (total == 0)
291 return (TAG_NOTAG);
292 curtag = taglist.tl_first;
293 curseq = 1;
294 return (TAG_FOUND);
295}
296
297/*
298 * Edit current tagged file.
299 */
300int
301edit_tagfile(void)
302{
303 if (curtag == NULL((void *)0))
304 return (1);
305 return (edit(curtag->tag_file));
306}
307
308/*
309 * Search for a tag.
310 * This is a stripped-down version of search().
311 * We don't use search() for several reasons:
312 * - We don't want to blow away any search string we may have saved.
313 * - The various regular-expression functions (from different systems:
314 * regcmp vs. re_comp) behave differently in the presence of
315 * parentheses (which are almost always found in a tag).
316 */
317static off_t
318ctagsearch(void)
319{
320 off_t pos, linepos;
321 off_t linenum;
322 int len;
323 char *line;
324
325 pos = ch_zero()(0);
326 linenum = find_linenum(pos);
327
328 for (;;) {
329 /*
330 * Get lines until we find a matching one or
331 * until we hit end-of-file.
332 */
333 if (abort_sigs())
334 return (-1);
335
336 /*
337 * Read the next line, and save the
338 * starting position of that line in linepos.
339 */
340 linepos = pos;
341 pos = forw_raw_line(pos, &line, (int *)NULL((void *)0));
342 if (linenum != 0)
343 linenum++;
344
345 if (pos == -1) {
346 /*
347 * We hit EOF without a match.
348 */
349 error("Tag not found", NULL((void *)0));
350 return (-1);
351 }
352
353 /*
354 * If we're using line numbers, we might as well
355 * remember the information we have now (the position
356 * and line number of the current line).
357 */
358 if (linenums)
359 add_lnum(linenum, pos);
360
361 /*
362 * Test the line to see if we have a match.
363 * Use strncmp because the pattern may be
364 * truncated (in the tags file) if it is too long.
365 * If tagendline is set, make sure we match all
366 * the way to end of line (no extra chars after the match).
367 */
368 len = strlen(curtag->tag_pattern);
369 if (strncmp(curtag->tag_pattern, line, len) == 0 &&
370 (!curtag->tag_endline || line[len] == '\0' ||
371 line[len] == '\r')) {
372 curtag->tag_linenum = find_linenum(linepos);
373 break;
374 }
375 }
376
377 return (linepos);
378}
379
380static int circular = 0; /* 1: circular tag structure */
381
382/*
383 * Return the filename required for the next tag in the queue that was setup
384 * by findctag(). The next call to ctagsearch() will try to position at the
385 * appropriate tag.
386 */
387static char *
388nextctag(void)
389{
390 struct tag *tp;
391
392 if (curtag == NULL((void *)0))
393 /* No tag loaded */
394 return (NULL((void *)0));
395
396 tp = curtag->next;
397 if (tp == TAG_END((struct tag *)&taglist)) {
398 if (!circular)
399 return (NULL((void *)0));
400 /* Wrapped around to the head of the queue */
401 curtag = taglist.tl_first;
402 curseq = 1;
403 } else {
404 curtag = tp;
405 curseq++;
406 }
407 return (curtag->tag_file);
408}
409
410/*
411 * Return the filename required for the previous ctag in the queue that was
412 * setup by findctag(). The next call to ctagsearch() will try to position
413 * at the appropriate tag.
414 */
415static char *
416prevctag(void)
417{
418 struct tag *tp;
419
420 if (curtag == NULL((void *)0))
421 /* No tag loaded */
422 return (NULL((void *)0));
423
424 tp = curtag->prev;
425 if (tp == TAG_END((struct tag *)&taglist)) {
426 if (!circular)
427 return (NULL((void *)0));
428 /* Wrapped around to the tail of the queue */
429 curtag = taglist.tl_last;
430 curseq = total;
431 } else {
432 curtag = tp;
433 curseq--;
434 }
435 return (curtag->tag_file);
436}