File: | src/lib/libutil/imsg-buffer.c |
Warning: | line 221, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: imsg-buffer.c,v 1.13 2021/03/31 17:42:24 eric Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> | |||
5 | * | |||
6 | * Permission to use, copy, modify, and distribute this software for any | |||
7 | * purpose with or without fee is hereby granted, provided that the above | |||
8 | * copyright notice and this permission notice appear in all copies. | |||
9 | * | |||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
17 | */ | |||
18 | ||||
19 | #include <sys/types.h> | |||
20 | #include <sys/queue.h> | |||
21 | #include <sys/socket.h> | |||
22 | #include <sys/uio.h> | |||
23 | ||||
24 | #include <limits.h> | |||
25 | #include <errno(*__errno()).h> | |||
26 | #include <stdlib.h> | |||
27 | #include <string.h> | |||
28 | #include <unistd.h> | |||
29 | ||||
30 | #include "imsg.h" | |||
31 | ||||
32 | static int ibuf_realloc(struct ibuf *, size_t); | |||
33 | static void ibuf_enqueue(struct msgbuf *, struct ibuf *); | |||
34 | static void ibuf_dequeue(struct msgbuf *, struct ibuf *); | |||
35 | ||||
36 | struct ibuf * | |||
37 | ibuf_open(size_t len) | |||
38 | { | |||
39 | struct ibuf *buf; | |||
40 | ||||
41 | if ((buf = calloc(1, sizeof(struct ibuf))) == NULL((void *)0)) | |||
42 | return (NULL((void *)0)); | |||
43 | if ((buf->buf = malloc(len)) == NULL((void *)0)) { | |||
44 | free(buf); | |||
45 | return (NULL((void *)0)); | |||
46 | } | |||
47 | buf->size = buf->max = len; | |||
48 | buf->fd = -1; | |||
49 | ||||
50 | return (buf); | |||
51 | } | |||
52 | ||||
53 | struct ibuf * | |||
54 | ibuf_dynamic(size_t len, size_t max) | |||
55 | { | |||
56 | struct ibuf *buf; | |||
57 | ||||
58 | if (max < len) | |||
59 | return (NULL((void *)0)); | |||
60 | ||||
61 | if ((buf = ibuf_open(len)) == NULL((void *)0)) | |||
62 | return (NULL((void *)0)); | |||
63 | ||||
64 | if (max > 0) | |||
65 | buf->max = max; | |||
66 | ||||
67 | return (buf); | |||
68 | } | |||
69 | ||||
70 | static int | |||
71 | ibuf_realloc(struct ibuf *buf, size_t len) | |||
72 | { | |||
73 | unsigned char *b; | |||
74 | ||||
75 | /* on static buffers max is eq size and so the following fails */ | |||
76 | if (buf->wpos + len > buf->max) { | |||
77 | errno(*__errno()) = ERANGE34; | |||
78 | return (-1); | |||
79 | } | |||
80 | ||||
81 | b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1); | |||
82 | if (b == NULL((void *)0)) | |||
83 | return (-1); | |||
84 | buf->buf = b; | |||
85 | buf->size = buf->wpos + len; | |||
86 | ||||
87 | return (0); | |||
88 | } | |||
89 | ||||
90 | int | |||
91 | ibuf_add(struct ibuf *buf, const void *data, size_t len) | |||
92 | { | |||
93 | if (buf->wpos + len > buf->size) | |||
94 | if (ibuf_realloc(buf, len) == -1) | |||
95 | return (-1); | |||
96 | ||||
97 | memcpy(buf->buf + buf->wpos, data, len); | |||
98 | buf->wpos += len; | |||
99 | return (0); | |||
100 | } | |||
101 | ||||
102 | void * | |||
103 | ibuf_reserve(struct ibuf *buf, size_t len) | |||
104 | { | |||
105 | void *b; | |||
106 | ||||
107 | if (buf->wpos + len > buf->size) | |||
108 | if (ibuf_realloc(buf, len) == -1) | |||
109 | return (NULL((void *)0)); | |||
110 | ||||
111 | b = buf->buf + buf->wpos; | |||
112 | buf->wpos += len; | |||
113 | return (b); | |||
114 | } | |||
115 | ||||
116 | void * | |||
117 | ibuf_seek(struct ibuf *buf, size_t pos, size_t len) | |||
118 | { | |||
119 | /* only allowed to seek in already written parts */ | |||
120 | if (pos + len > buf->wpos) | |||
121 | return (NULL((void *)0)); | |||
122 | ||||
123 | return (buf->buf + pos); | |||
124 | } | |||
125 | ||||
126 | size_t | |||
127 | ibuf_size(struct ibuf *buf) | |||
128 | { | |||
129 | return (buf->wpos); | |||
130 | } | |||
131 | ||||
132 | size_t | |||
133 | ibuf_left(struct ibuf *buf) | |||
134 | { | |||
135 | return (buf->max - buf->wpos); | |||
136 | } | |||
137 | ||||
138 | void | |||
139 | ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) | |||
140 | { | |||
141 | ibuf_enqueue(msgbuf, buf); | |||
142 | } | |||
143 | ||||
144 | int | |||
145 | ibuf_write(struct msgbuf *msgbuf) | |||
146 | { | |||
147 | struct iovec iov[IOV_MAX1024]; | |||
148 | struct ibuf *buf; | |||
149 | unsigned int i = 0; | |||
150 | ssize_t n; | |||
151 | ||||
152 | memset(&iov, 0, sizeof(iov)); | |||
153 | TAILQ_FOREACH(buf, &msgbuf->bufs, entry)for((buf) = ((&msgbuf->bufs)->tqh_first); (buf) != ( (void *)0); (buf) = ((buf)->entry.tqe_next)) { | |||
154 | if (i >= IOV_MAX1024) | |||
155 | break; | |||
156 | iov[i].iov_base = buf->buf + buf->rpos; | |||
157 | iov[i].iov_len = buf->wpos - buf->rpos; | |||
158 | i++; | |||
159 | } | |||
160 | ||||
161 | again: | |||
162 | if ((n = writev(msgbuf->fd, iov, i)) == -1) { | |||
163 | if (errno(*__errno()) == EINTR4) | |||
164 | goto again; | |||
165 | if (errno(*__errno()) == ENOBUFS55) | |||
166 | errno(*__errno()) = EAGAIN35; | |||
167 | return (-1); | |||
168 | } | |||
169 | ||||
170 | if (n == 0) { /* connection closed */ | |||
171 | errno(*__errno()) = 0; | |||
172 | return (0); | |||
173 | } | |||
174 | ||||
175 | msgbuf_drain(msgbuf, n); | |||
176 | ||||
177 | return (1); | |||
178 | } | |||
179 | ||||
180 | void | |||
181 | ibuf_free(struct ibuf *buf) | |||
182 | { | |||
183 | if (buf
| |||
184 | return; | |||
185 | freezero(buf->buf, buf->size); | |||
186 | free(buf); | |||
187 | } | |||
188 | ||||
189 | void | |||
190 | msgbuf_init(struct msgbuf *msgbuf) | |||
191 | { | |||
192 | msgbuf->queued = 0; | |||
193 | msgbuf->fd = -1; | |||
194 | TAILQ_INIT(&msgbuf->bufs)do { (&msgbuf->bufs)->tqh_first = ((void *)0); (& msgbuf->bufs)->tqh_last = &(&msgbuf->bufs)-> tqh_first; } while (0); | |||
195 | } | |||
196 | ||||
197 | void | |||
198 | msgbuf_drain(struct msgbuf *msgbuf, size_t n) | |||
199 | { | |||
200 | struct ibuf *buf, *next; | |||
201 | ||||
202 | for (buf = TAILQ_FIRST(&msgbuf->bufs)((&msgbuf->bufs)->tqh_first); buf != NULL((void *)0) && n > 0; | |||
203 | buf = next) { | |||
204 | next = TAILQ_NEXT(buf, entry)((buf)->entry.tqe_next); | |||
205 | if (buf->rpos + n >= buf->wpos) { | |||
206 | n -= buf->wpos - buf->rpos; | |||
207 | ibuf_dequeue(msgbuf, buf); | |||
208 | } else { | |||
209 | buf->rpos += n; | |||
210 | n = 0; | |||
211 | } | |||
212 | } | |||
213 | } | |||
214 | ||||
215 | void | |||
216 | msgbuf_clear(struct msgbuf *msgbuf) | |||
217 | { | |||
218 | struct ibuf *buf; | |||
219 | ||||
220 | while ((buf = TAILQ_FIRST(&msgbuf->bufs)((&msgbuf->bufs)->tqh_first)) != NULL((void *)0)) | |||
| ||||
221 | ibuf_dequeue(msgbuf, buf); | |||
| ||||
222 | } | |||
223 | ||||
224 | int | |||
225 | msgbuf_write(struct msgbuf *msgbuf) | |||
226 | { | |||
227 | struct iovec iov[IOV_MAX1024]; | |||
228 | struct ibuf *buf, *buf0 = NULL((void *)0); | |||
229 | unsigned int i = 0; | |||
230 | ssize_t n; | |||
231 | struct msghdr msg; | |||
232 | struct cmsghdr *cmsg; | |||
233 | union { | |||
234 | struct cmsghdr hdr; | |||
235 | char buf[CMSG_SPACE(sizeof(int))((((unsigned long)(sizeof(struct cmsghdr)) + (sizeof(long) - 1 )) &~(sizeof(long) - 1)) + (((unsigned long)(sizeof(int)) + (sizeof(long) - 1)) &~(sizeof(long) - 1)))]; | |||
236 | } cmsgbuf; | |||
237 | ||||
238 | memset(&iov, 0, sizeof(iov)); | |||
239 | memset(&msg, 0, sizeof(msg)); | |||
240 | memset(&cmsgbuf, 0, sizeof(cmsgbuf)); | |||
241 | TAILQ_FOREACH(buf, &msgbuf->bufs, entry)for((buf) = ((&msgbuf->bufs)->tqh_first); (buf) != ( (void *)0); (buf) = ((buf)->entry.tqe_next)) { | |||
242 | if (i >= IOV_MAX1024) | |||
243 | break; | |||
244 | if (i > 0 && buf->fd != -1) | |||
245 | break; | |||
246 | iov[i].iov_base = buf->buf + buf->rpos; | |||
247 | iov[i].iov_len = buf->wpos - buf->rpos; | |||
248 | i++; | |||
249 | if (buf->fd != -1) | |||
250 | buf0 = buf; | |||
251 | } | |||
252 | ||||
253 | msg.msg_iov = iov; | |||
254 | msg.msg_iovlen = i; | |||
255 | ||||
256 | if (buf0 != NULL((void *)0)) { | |||
257 | msg.msg_control = (caddr_t)&cmsgbuf.buf; | |||
258 | msg.msg_controllen = sizeof(cmsgbuf.buf); | |||
259 | cmsg = CMSG_FIRSTHDR(&msg)((&msg)->msg_controllen >= sizeof(struct cmsghdr) ? (struct cmsghdr *)(&msg)->msg_control : (struct cmsghdr *)((void *)0)); | |||
260 | cmsg->cmsg_len = CMSG_LEN(sizeof(int))((((unsigned long)(sizeof(struct cmsghdr)) + (sizeof(long) - 1 )) &~(sizeof(long) - 1)) + (sizeof(int))); | |||
261 | cmsg->cmsg_level = SOL_SOCKET0xffff; | |||
262 | cmsg->cmsg_type = SCM_RIGHTS0x01; | |||
263 | *(int *)CMSG_DATA(cmsg)((unsigned char *)(cmsg) + (((unsigned long)(sizeof(struct cmsghdr )) + (sizeof(long) - 1)) &~(sizeof(long) - 1))) = buf0->fd; | |||
264 | } | |||
265 | ||||
266 | again: | |||
267 | if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { | |||
268 | if (errno(*__errno()) == EINTR4) | |||
269 | goto again; | |||
270 | if (errno(*__errno()) == ENOBUFS55) | |||
271 | errno(*__errno()) = EAGAIN35; | |||
272 | return (-1); | |||
273 | } | |||
274 | ||||
275 | if (n == 0) { /* connection closed */ | |||
276 | errno(*__errno()) = 0; | |||
277 | return (0); | |||
278 | } | |||
279 | ||||
280 | /* | |||
281 | * assumption: fd got sent if sendmsg sent anything | |||
282 | * this works because fds are passed one at a time | |||
283 | */ | |||
284 | if (buf0 != NULL((void *)0)) { | |||
285 | close(buf0->fd); | |||
286 | buf0->fd = -1; | |||
287 | } | |||
288 | ||||
289 | msgbuf_drain(msgbuf, n); | |||
290 | ||||
291 | return (1); | |||
292 | } | |||
293 | ||||
294 | static void | |||
295 | ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) | |||
296 | { | |||
297 | TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry)do { (buf)->entry.tqe_next = ((void *)0); (buf)->entry. tqe_prev = (&msgbuf->bufs)->tqh_last; *(&msgbuf ->bufs)->tqh_last = (buf); (&msgbuf->bufs)->tqh_last = &(buf)->entry.tqe_next; } while (0); | |||
298 | msgbuf->queued++; | |||
299 | } | |||
300 | ||||
301 | static void | |||
302 | ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) | |||
303 | { | |||
304 | TAILQ_REMOVE(&msgbuf->bufs, buf, entry)do { if (((buf)->entry.tqe_next) != ((void *)0)) (buf)-> entry.tqe_next->entry.tqe_prev = (buf)->entry.tqe_prev; else (&msgbuf->bufs)->tqh_last = (buf)->entry.tqe_prev ; *(buf)->entry.tqe_prev = (buf)->entry.tqe_next; ; ; } while (0); | |||
305 | ||||
306 | if (buf->fd != -1) | |||
307 | close(buf->fd); | |||
308 | ||||
309 | msgbuf->queued--; | |||
310 | ibuf_free(buf); | |||
311 | } |