Bug Summary

File:obj-scan-build/libftpconn/../../libftpconn/names.c
Location:line 137, column 25
Description:Attempt to free released memory

Annotated Source Code

1/* Fetch directory file names
2
3 Copyright (C) 1997 Free Software Foundation, Inc.
4
5 Written by Miles Bader <miles@gnu.ai.mit.edu>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2, or (at
10 your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include <unistd.h>
22#include <string.h>
23#include <errno(*__errno_location ()).h>
24
25#include <ftpconn.h>
26
27struct get_names_state
28{
29 char *name; /* Last read (maybe partial) name. */
30 size_t name_len; /* Valid length of NAME, *not including* '\0'. */
31 size_t name_alloced; /* Allocated size of NAME (>= NAME_LEN). */
32 int name_partial; /* True if NAME isn't complete. */
33
34 size_t buf_len; /* Length of contents in BUF. */
35 char buf[7000];
36};
37
38/* Start an operation to get a list of filenames in the directory NAME, and
39 return a file-descriptor for reading on, and a state structure in STATE
40 suitable for passing to cont_get_names. */
41error_t
42ftp_conn_start_get_names (struct ftp_conn *conn,
43 const char *name, int *fd, void **state)
44{
45 error_t err;
46 struct get_names_state *s = malloc (sizeof (struct get_names_state));
47
48 if (! s)
49 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
50
51 err = ftp_conn_start_list (conn, name, fd);
52
53 if (err)
54 free (s);
55 else
56 {
57 s->name = 0;
58 s->name_len = s->name_alloced = 0;
59 s->name_partial = 0;
60 s->buf_len = 0;
61 *state = s;
62 }
63
64 return err;
65}
66
67/* Read filenames from FD, calling ADD_NAME for each new NAME (HOOK is passed
68 to ADD_NAME). FD and STATE should be returned from start_get_names. If
69 this function returns EAGAIN, then it should be called again to finish the
70 job (possibly after calling select on FD); if it returns 0, then it is
71 finishe,d and FD and STATE are deallocated. */
72error_t
73ftp_conn_cont_get_names (struct ftp_conn *conn, int fd, void *state,
74 ftp_conn_add_name_fun_t add_name, void *hook)
75{
76 char *p, *nl;
77 ssize_t rd;
78 size_t name_len;
79 error_t err = 0;
80 struct get_names_state *s = state;
81 int (*icheck) (struct ftp_conn *conn) = conn->hooks->interrupt_check;
82
83 /* We always consume full lines, so we know that we have to read more when
84 we first get called. */
85 rd = read (fd, s->buf + s->buf_len, sizeof (s->buf) - s->buf_len);
86 if (rd < 0)
3
Assuming 'rd' is >= 0
4
Taking false branch
87 {
88 err = errno(*__errno_location ());
89 goto finished;
90 }
91
92 if (icheck && (*icheck) (conn))
93 {
94 err = EINTR((0x10 << 26) | ((4) & 0x3fff));
95 goto finished;
96 }
97
98 if (rd == 0)
5
Assuming 'rd' is not equal to 0
6
Taking false branch
99 /* EOF */
100 if (s->buf_len == 0)
101 /* We're done! Clean up and return the result in NAMES. */
102 goto finished;
103 else
104 /* Partial line at end of file? */
105 nl = s->buf + s->buf_len;
106 else
107 /* Look for a new line in what we read (we know that there weren't any in
108 the buffer before that). */
109 {
110 nl = memchr (s->buf + s->buf_len, '\n', rd);
111 s->buf_len += rd;
112 }
113
114 if (!nl && s->buf_len < sizeof (s->buf))
7
Assuming 'nl' is non-null
115 /* We didn't find any newlines (which implies we didn't hit EOF), and we
116 still have room to grow the buffer, so just wait until next time to do
117 anything. */
118 return EAGAIN((0x10 << 26) | ((35) & 0x3fff));
119
120 /* Where we start parsing. */
121 p = s->buf;
122
123 do
17
Loop condition is true. Execution continues on line 126
124 {
125 /* Fill in S->name, possibly extending it from a previous buffer. */
126 name_len = (nl ? nl - p : s->buf + s->buf_len - p);
8
'?' condition is true
18
'?' condition is true
127 if (name_len > 0 && p[name_len - 1] == '\r')
9
Assuming 'name_len' is <= 0
19
Assuming 'name_len' is > 0
20
Taking false branch
128 name_len--;
129 if (name_len > 0)
10
Taking false branch
21
Taking true branch
130 /* Extending s->name. */
131 {
132 size_t old_len = s->name_len;
133 size_t total_len = old_len + name_len + 1;
134
135 if (total_len > s->name_alloced)
22
Taking true branch
136 {
137 char *new_name = realloc (s->name, total_len);
23
Attempt to free released memory
138 if (! new_name)
139 goto enomem;
140 s->name = new_name;
141 s->name_alloced = total_len;
142 }
143
144 strncpy (s->name + old_len, p, name_len);
145 s->name[old_len + name_len] = '\0';
146 s->name_len = total_len - 1;
147 }
148
149 if (nl)
11
Taking true branch
150 {
151 char *name = s->name;
152
153 if (conn->syshooks.basename)
12
Taking false branch
154 /* Fixup any screwy names returned by the server. */
155 {
156 err = (*conn->syshooks.basename) (conn, &name);
157 if (err)
158 goto finished;
159 }
160
161 /* Call the callback function to process the current entry. */
162 err = (*add_name) (name, hook);
163
164 if (name < s->name || name > s->name + s->name_len)
13
Taking true branch
165 /* User-allocated NAME from the fix_nlist_name hook. */
166 free (name);
14
Memory is released
167
168 if (err)
15
Assuming 'err' is 0
16
Taking false branch
169 goto finished;
170
171 s->name_len = 0;
172 s->name_partial = 0;
173
174 p = nl + 1;
175 nl = memchr (p, '\n', s->buf + s->buf_len - p);
176 }
177 else
178 /* We found no newline, so the name extends past what we read; we'll
179 try to read more next time. */
180 {
181 s->name_partial = 1;
182 /* Skip over the partial name for the next iteration. */
183 p += name_len;
184 }
185 }
186 while (nl);
187
188 /* Move any remaining characters in the buffer to the beginning for the
189 next call. */
190 s->buf_len -= (p - s->buf);
191 if (s->buf_len > 0)
192 memmove (s->buf, p, s->buf_len);
193
194 /* Try again later. */
195 return EAGAIN((0x10 << 26) | ((35) & 0x3fff));
196
197enomem:
198 /* Some memory allocation failed. */
199 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
200
201finished:
202 /* We're finished (with an error if ERR != 0), deallocate everything &
203 return. */
204 if (s->name)
205 free (s->name);
206 free (s);
207 close (fd);
208
209 if (err && rd > 0)
210 ftp_conn_abort (conn);
211 else if (err)
212 ftp_conn_finish_transfer (conn);
213 else
214 err = ftp_conn_finish_transfer (conn);
215
216 return err;
217}
218
219/* Get a list of names in the directory NAME, calling ADD_NAME for each one
220 (HOOK is passed to ADD_NAME). This function may block. */
221error_t
222ftp_conn_get_names (struct ftp_conn *conn, const char *name,
223 ftp_conn_add_name_fun_t add_name, void *hook)
224{
225 int fd;
226 void *state;
227 error_t err = ftp_conn_start_get_names (conn, name, &fd, &state);
228
229 if (err)
1
Taking false branch
230 return err;
231
232 do
233 err = ftp_conn_cont_get_names (conn, fd, state, add_name, hook);
2
Calling 'ftp_conn_cont_get_names'
234 while (err == EAGAIN((0x10 << 26) | ((35) & 0x3fff)));
235
236 return err;
237}