Bug Summary

File:src/usr.bin/vi/build/../common/cut.c
Warning:line 107, column 10
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 cut.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/vi/build/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/vi/build -I /usr/src/usr.bin/vi/build/../include -I . -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/vi/build/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/vi/build/../common/cut.c
1/* $OpenBSD: cut.c,v 1.17 2017/04/18 01:45:35 deraadt Exp $ */
2
3/*-
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
8 *
9 * See the LICENSE file for redistribution information.
10 */
11
12#include "config.h"
13
14#include <sys/types.h>
15#include <sys/queue.h>
16
17#include <bitstring.h>
18#include <ctype.h>
19#include <errno(*__errno()).h>
20#include <fcntl.h>
21#include <limits.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "common.h"
27
28static void cb_rotate(SCR *);
29
30/*
31 * cut --
32 * Put a range of lines/columns into a TEXT buffer.
33 *
34 * There are two buffer areas, both found in the global structure. The first
35 * is the linked list of all the buffers the user has named, the second is the
36 * unnamed buffer storage. There is a pointer, too, which is the current
37 * default buffer, i.e. it may point to the unnamed buffer or a named buffer
38 * depending on into what buffer the last text was cut. Logically, in both
39 * delete and yank operations, if the user names a buffer, the text is cut
40 * into it. If it's a delete of information on more than a single line, the
41 * contents of the numbered buffers are rotated up one, the contents of the
42 * buffer named '9' are discarded, and the text is cut into the buffer named
43 * '1'. The text is always cut into the unnamed buffer.
44 *
45 * In all cases, upper-case buffer names are the same as lower-case names,
46 * with the exception that they cause the buffer to be appended to instead
47 * of replaced. Note, however, that if text is appended to a buffer, the
48 * default buffer only contains the appended text, not the entire contents
49 * of the buffer.
50 *
51 * !!!
52 * The contents of the default buffer would disappear after most operations
53 * in historic vi. It's unclear that this is useful, so we don't bother.
54 *
55 * When users explicitly cut text into the numeric buffers, historic vi became
56 * genuinely strange. I've never been able to figure out what was supposed to
57 * happen. It behaved differently if you deleted text than if you yanked text,
58 * and, in the latter case, the text was appended to the buffer instead of
59 * replacing the contents. Hopefully it's not worth getting right, and here
60 * we just treat the numeric buffers like any other named buffer.
61 *
62 * PUBLIC: int cut(SCR *, CHAR_T *, MARK *, MARK *, int);
63 */
64int
65cut(SCR *sp, CHAR_T *namep, MARK *fm, MARK *tm, int flags)
66{
67 CB *cbp;
68 CHAR_T name = '1'; /* default numeric buffer */
69 recno_t lno;
70 int append, copy_one, copy_def;
71
72 /*
73 * If the user specified a buffer, put it there. (This may require
74 * a copy into the numeric buffers. We do the copy so that we don't
75 * have to reference count and so we don't have to deal with things
76 * like appends to buffers that are used multiple times.)
77 *
78 * Otherwise, if it's supposed to be put in a numeric buffer (usually
79 * a delete) put it there. The rules for putting things in numeric
80 * buffers were historically a little strange. There were three cases.
81 *
82 * 1: Some motions are always line mode motions, which means
83 * that the cut always goes into the numeric buffers.
84 * 2: Some motions aren't line mode motions, e.g. d10w, but
85 * can cross line boundaries. For these commands, if the
86 * cut crosses a line boundary, it goes into the numeric
87 * buffers. This includes most of the commands.
88 * 3: Some motions aren't line mode motions, e.g. d`<char>,
89 * but always go into the numeric buffers, regardless. This
90 * was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A.
91 *
92 * Otherwise, put it in the unnamed buffer.
93 */
94 append = copy_one = copy_def = 0;
95 if (namep != NULL((void *)0)) {
1
Assuming 'namep' is equal to NULL
2
Taking false branch
96 name = *namep;
97 if (LF_ISSET(CUT_NUMREQ)((flags) & ((0x04))) || (LF_ISSET(CUT_NUMOPT)((flags) & ((0x02))) &&
98 (LF_ISSET(CUT_LINEMODE)((flags) & ((0x01))) || fm->lno != tm->lno))) {
99 copy_one = 1;
100 cb_rotate(sp);
101 }
102 if ((append = isupper(name)) == 1) {
103 if (!copy_one)
104 copy_def = 1;
105 name = tolower(name);
106 }
107namecb: CBNAME(sp, cbp, name){ CHAR_T L__name; L__name = isupper(name) ? tolower(name) : (
name); for(((cbp)) = ((&(sp)->gp->cutq)->lh_first
); ((cbp))!= ((void *)0); ((cbp)) = (((cbp))->q.le_next)) if
((cbp)->name == L__name) break; }
;
21
'?' condition is false
22
Loop condition is true. Entering loop body
23
Use of memory after it is freed
108 } else if (LF_ISSET(CUT_NUMREQ)((flags) & ((0x04))) || (LF_ISSET(CUT_NUMOPT)((flags) & ((0x02))) &&
3
Assuming the condition is false
4
Assuming the condition is true
7
Taking true branch
109 (LF_ISSET(CUT_LINEMODE)((flags) & ((0x01))) || fm->lno != tm->lno))) {
5
Assuming the condition is false
6
Assuming 'fm->lno' is not equal to 'tm->lno'
110 /* Copy into numeric buffer 1. */
111 cb_rotate(sp);
8
Calling 'cb_rotate'
19
Returning; memory was released
112 goto namecb;
20
Control jumps to line 107
113 } else
114 cbp = &sp->gp->dcb_store;
115
116copyloop:
117 /*
118 * If this is a new buffer, create it and add it into the list.
119 * Otherwise, if it's not an append, free its current contents.
120 */
121 if (cbp == NULL((void *)0)) {
122 CALLOC_RET(sp, cbp, 1, sizeof(CB)){ if (((cbp) = calloc((1), (sizeof(CB)))) == ((void *)0)) { msgq
((sp), M_SYSERR, ((void *)0)); return (1); } }
;
123 cbp->name = name;
124 TAILQ_INIT(&cbp->textq)do { (&cbp->textq)->tqh_first = ((void *)0); (&
cbp->textq)->tqh_last = &(&cbp->textq)->tqh_first
; } while (0)
;
125 LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q)do { if (((cbp)->q.le_next = (&sp->gp->cutq)->
lh_first) != ((void *)0)) (&sp->gp->cutq)->lh_first
->q.le_prev = &(cbp)->q.le_next; (&sp->gp->
cutq)->lh_first = (cbp); (cbp)->q.le_prev = &(&
sp->gp->cutq)->lh_first; } while (0)
;
126 } else if (!append) {
127 text_lfree(&cbp->textq);
128 cbp->len = 0;
129 cbp->flags = 0;
130 }
131
132
133 /* In line mode, it's pretty easy, just cut the lines. */
134 if (LF_ISSET(CUT_LINEMODE)((flags) & ((0x01)))) {
135 cbp->flags |= CB_LMODE0x01;
136 for (lno = fm->lno; lno <= tm->lno; ++lno)
137 if (cut_line(sp, lno, 0, CUT_LINE_TO_EOL((size_t) -1), cbp))
138 goto cut_line_err;
139 } else {
140 /*
141 * Get the first line. A length of CUT_LINE_TO_EOL causes
142 * cut_line() to cut from the MARK to the end of the line.
143 */
144 if (cut_line(sp, fm->lno, fm->cno, fm->lno != tm->lno ?
145 CUT_LINE_TO_EOL((size_t) -1) : (tm->cno - fm->cno) + 1, cbp))
146 goto cut_line_err;
147
148 /* Get the intermediate lines. */
149 for (lno = fm->lno; ++lno < tm->lno;)
150 if (cut_line(sp, lno, 0, CUT_LINE_TO_EOL((size_t) -1), cbp))
151 goto cut_line_err;
152
153 /* Get the last line. */
154 if (tm->lno != fm->lno &&
155 cut_line(sp, lno, 0, tm->cno + 1, cbp))
156 goto cut_line_err;
157 }
158
159 append = 0; /* Only append to the named buffer. */
160 sp->gp->dcbp = cbp; /* Repoint the default buffer on each pass. */
161
162 if (copy_one) { /* Copy into numeric buffer 1. */
163 CBNAME(sp, cbp, name){ CHAR_T L__name; L__name = isupper(name) ? tolower(name) : (
name); for(((cbp)) = ((&(sp)->gp->cutq)->lh_first
); ((cbp))!= ((void *)0); ((cbp)) = (((cbp))->q.le_next)) if
((cbp)->name == L__name) break; }
;
164 copy_one = 0;
165 goto copyloop;
166 }
167 if (copy_def) { /* Copy into the default buffer. */
168 cbp = &sp->gp->dcb_store;
169 copy_def = 0;
170 goto copyloop;
171 }
172 return (0);
173
174cut_line_err:
175 text_lfree(&cbp->textq);
176 cbp->len = 0;
177 cbp->flags = 0;
178 return (1);
179}
180
181/*
182 * cb_rotate --
183 * Rotate the numbered buffers up one.
184 */
185static void
186cb_rotate(SCR *sp)
187{
188 CB *cbp, *del_cbp;
189
190 del_cbp = NULL((void *)0);
191 LIST_FOREACH(cbp, &sp->gp->cutq, q)for((cbp) = ((&sp->gp->cutq)->lh_first); (cbp)!=
((void *)0); (cbp) = ((cbp)->q.le_next))
9
Assuming 'cbp' is not equal to null
10
Loop condition is true. Entering loop body
13
Assuming 'cbp' is equal to null
14
Loop condition is false. Execution continues on line 221
192 switch(cbp->name) {
11
Control jumps to 'case 57:' at line 217
193 case '1':
194 cbp->name = '2';
195 break;
196 case '2':
197 cbp->name = '3';
198 break;
199 case '3':
200 cbp->name = '4';
201 break;
202 case '4':
203 cbp->name = '5';
204 break;
205 case '5':
206 cbp->name = '6';
207 break;
208 case '6':
209 cbp->name = '7';
210 break;
211 case '7':
212 cbp->name = '8';
213 break;
214 case '8':
215 cbp->name = '9';
216 break;
217 case '9':
218 del_cbp = cbp;
219 break;
12
Execution continues on line 191
220 }
221 if (del_cbp
14.1
'del_cbp' is not equal to NULL
!= NULL((void *)0)) {
15
Taking true branch
222 LIST_REMOVE(del_cbp, q)do { if ((del_cbp)->q.le_next != ((void *)0)) (del_cbp)->
q.le_next->q.le_prev = (del_cbp)->q.le_prev; *(del_cbp)
->q.le_prev = (del_cbp)->q.le_next; ; ; } while (0)
;
16
Taking false branch
17
Loop condition is false. Exiting loop
223 text_lfree(&del_cbp->textq);
224 free(del_cbp);
18
Memory is released
225 }
226}
227
228/*
229 * cut_line --
230 * Cut a portion of a single line.
231 *
232 * PUBLIC: int cut_line(SCR *, recno_t, size_t, size_t, CB *);
233 */
234int
235cut_line(SCR *sp, recno_t lno, size_t fcno, size_t clen, CB *cbp)
236{
237 TEXT *tp;
238 size_t len;
239 char *p;
240
241 /* Get the line. */
242 if (db_get(sp, lno, DBG_FATAL0x001, &p, &len))
243 return (1);
244
245 /* Create a TEXT structure that can hold the entire line. */
246 if ((tp = text_init(sp, NULL((void *)0), 0, len)) == NULL((void *)0))
247 return (1);
248
249 /*
250 * If the line isn't empty and it's not the entire line,
251 * copy the portion we want, and reset the TEXT length.
252 */
253 if (len != 0) {
254 if (clen == CUT_LINE_TO_EOL((size_t) -1))
255 clen = len - fcno;
256 memcpy(tp->lb, p + fcno, clen);
257 tp->len = clen;
258 }
259
260 /* Append to the end of the cut buffer. */
261 TAILQ_INSERT_TAIL(&cbp->textq, tp, q)do { (tp)->q.tqe_next = ((void *)0); (tp)->q.tqe_prev =
(&cbp->textq)->tqh_last; *(&cbp->textq)->
tqh_last = (tp); (&cbp->textq)->tqh_last = &(tp
)->q.tqe_next; } while (0)
;
262 cbp->len += tp->len;
263
264 return (0);
265}
266
267/*
268 * cut_close --
269 * Discard all cut buffers.
270 *
271 * PUBLIC: void cut_close(GS *);
272 */
273void
274cut_close(GS *gp)
275{
276 CB *cbp;
277
278 /* Free cut buffer list. */
279 while ((cbp = LIST_FIRST(&gp->cutq)((&gp->cutq)->lh_first)) != NULL((void *)0)) {
280 if (!TAILQ_EMPTY(&cbp->textq)(((&cbp->textq)->tqh_first) == ((void *)0)))
281 text_lfree(&cbp->textq);
282 LIST_REMOVE(cbp, q)do { if ((cbp)->q.le_next != ((void *)0)) (cbp)->q.le_next
->q.le_prev = (cbp)->q.le_prev; *(cbp)->q.le_prev = (
cbp)->q.le_next; ; ; } while (0)
;
283 free(cbp);
284 }
285
286 /* Free default cut storage. */
287 cbp = &gp->dcb_store;
288 if (!TAILQ_EMPTY(&cbp->textq)(((&cbp->textq)->tqh_first) == ((void *)0)))
289 text_lfree(&cbp->textq);
290}
291
292/*
293 * text_init --
294 * Allocate a new TEXT structure.
295 *
296 * PUBLIC: TEXT *text_init(SCR *, const char *, size_t, size_t);
297 */
298TEXT *
299text_init(SCR *sp, const char *p, size_t len, size_t total_len)
300{
301 TEXT *tp;
302
303 CALLOC(sp, tp, 1, sizeof(TEXT)){ if (((tp) = calloc((1), (sizeof(TEXT)))) == ((void *)0)) msgq
((sp), M_SYSERR, ((void *)0)); }
;
304 if (tp == NULL((void *)0))
305 return (NULL((void *)0));
306 /* ANSI C doesn't define a call to malloc(3) for 0 bytes. */
307 if ((tp->lb_len = total_len) != 0) {
308 MALLOC(sp, tp->lb, tp->lb_len){ if (((tp->lb) = malloc(tp->lb_len)) == ((void *)0)) msgq
((sp), M_SYSERR, ((void *)0)); }
;
309 if (tp->lb == NULL((void *)0)) {
310 free(tp);
311 return (NULL((void *)0));
312 }
313 if (p != NULL((void *)0) && len != 0)
314 memcpy(tp->lb, p, len);
315 }
316 tp->len = len;
317 return (tp);
318}
319
320/*
321 * text_lfree --
322 * Free a chain of text structures.
323 *
324 * PUBLIC: void text_lfree(TEXTH *);
325 */
326void
327text_lfree(TEXTH *headp)
328{
329 TEXT *tp;
330
331 while ((tp = TAILQ_FIRST(headp)((headp)->tqh_first))) {
332 TAILQ_REMOVE(headp, tp, q)do { if (((tp)->q.tqe_next) != ((void *)0)) (tp)->q.tqe_next
->q.tqe_prev = (tp)->q.tqe_prev; else (headp)->tqh_last
= (tp)->q.tqe_prev; *(tp)->q.tqe_prev = (tp)->q.tqe_next
; ; ; } while (0)
;
333 text_free(tp);
334 }
335}
336
337/*
338 * text_free --
339 * Free a text structure.
340 *
341 * PUBLIC: void text_free(TEXT *);
342 */
343void
344text_free(TEXT *tp)
345{
346 free(tp->lb);
347 free(tp);
348}