Coverage Report

Created: 2024-05-20 01:00

/src/openiked-portable/compat/imsg-buffer.c
Line
Count
Source (jump to first uncovered line)
1
/*  $OpenBSD: imsg-buffer.c,v 1.16 2023/06/19 17:19:50 claudio 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.h>
26
#include <endian.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <unistd.h>
30
31
#include "openbsd-compat.h"
32
#include "imsg.h"
33
34
static int  ibuf_realloc(struct ibuf *, size_t);
35
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
36
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
37
static void msgbuf_drain(struct msgbuf *, size_t);
38
39
struct ibuf *
40
ibuf_open(size_t len)
41
0
{
42
0
  struct ibuf *buf;
43
44
0
  if (len == 0) {
45
0
    errno = EINVAL;
46
0
    return (NULL);
47
0
  }
48
0
  if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
49
0
    return (NULL);
50
0
  if ((buf->buf = calloc(len, 1)) == NULL) {
51
0
    free(buf);
52
0
    return (NULL);
53
0
  }
54
0
  buf->size = buf->max = len;
55
0
  buf->fd = -1;
56
57
0
  return (buf);
58
0
}
59
60
struct ibuf *
61
ibuf_dynamic(size_t len, size_t max)
62
26.9k
{
63
26.9k
  struct ibuf *buf;
64
65
26.9k
  if (max < len) {
66
0
    errno = EINVAL;
67
0
    return (NULL);
68
0
  }
69
70
26.9k
  if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
71
0
    return (NULL);
72
26.9k
  if (len > 0) {
73
25.0k
    if ((buf->buf = calloc(len, 1)) == NULL) {
74
0
      free(buf);
75
0
      return (NULL);
76
0
    }
77
25.0k
  }
78
26.9k
  buf->size = len;
79
26.9k
  buf->max = max;
80
26.9k
  buf->fd = -1;
81
82
26.9k
  return (buf);
83
26.9k
}
84
85
static int
86
ibuf_realloc(struct ibuf *buf, size_t len)
87
0
{
88
0
  unsigned char *b;
89
90
  /* on static buffers max is eq size and so the following fails */
91
0
  if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
92
0
    errno = ERANGE;
93
0
    return (-1);
94
0
  }
95
96
0
  b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
97
0
  if (b == NULL)
98
0
    return (-1);
99
0
  buf->buf = b;
100
0
  buf->size = buf->wpos + len;
101
102
0
  return (0);
103
0
}
104
105
void *
106
ibuf_reserve(struct ibuf *buf, size_t len)
107
25.0k
{
108
25.0k
  void  *b;
109
110
25.0k
  if (len > SIZE_MAX - buf->wpos) {
111
0
    errno = ERANGE;
112
0
    return (NULL);
113
0
  }
114
115
25.0k
  if (buf->wpos + len > buf->size)
116
0
    if (ibuf_realloc(buf, len) == -1)
117
0
      return (NULL);
118
119
25.0k
  b = buf->buf + buf->wpos;
120
25.0k
  buf->wpos += len;
121
25.0k
  memset(b, 0, len);
122
25.0k
  return (b);
123
25.0k
}
124
125
int
126
ibuf_add(struct ibuf *buf, const void *data, size_t len)
127
25.0k
{
128
25.0k
  void *b;
129
130
25.0k
  if ((b = ibuf_reserve(buf, len)) == NULL)
131
0
    return (-1);
132
133
25.0k
  memcpy(b, data, len);
134
25.0k
  return (0);
135
25.0k
}
136
137
138
int
139
ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
140
0
{
141
0
  return ibuf_add(buf, from->buf, from->wpos);
142
0
}
143
144
int
145
ibuf_add_n8(struct ibuf *buf, uint64_t value)
146
0
{
147
0
  uint8_t v;
148
149
0
  if (value > UINT8_MAX) {
150
0
    errno = EINVAL;
151
0
    return (-1);
152
0
  }
153
0
  v = value;
154
0
  return ibuf_add(buf, &v, sizeof(v));
155
0
}
156
157
int
158
ibuf_add_n16(struct ibuf *buf, uint64_t value)
159
0
{
160
0
  uint16_t v;
161
162
0
  if (value > UINT16_MAX) {
163
0
    errno = EINVAL;
164
0
    return (-1);
165
0
  }
166
0
  v = htobe16(value);
167
0
  return ibuf_add(buf, &v, sizeof(v));
168
0
}
169
170
int
171
ibuf_add_n32(struct ibuf *buf, uint64_t value)
172
0
{
173
0
  uint32_t v;
174
175
0
  if (value > UINT32_MAX) {
176
0
    errno = EINVAL;
177
0
    return (-1);
178
0
  }
179
0
  v = htobe32(value);
180
0
  return ibuf_add(buf, &v, sizeof(v));
181
0
}
182
183
int
184
ibuf_add_n64(struct ibuf *buf, uint64_t value)
185
0
{
186
0
  value = htobe64(value);
187
0
  return ibuf_add(buf, &value, sizeof(value));
188
0
}
189
190
int
191
ibuf_add_zero(struct ibuf *buf, size_t len)
192
0
{
193
0
  void *b;
194
195
0
  if ((b = ibuf_reserve(buf, len)) == NULL)
196
0
    return (-1);
197
0
  return (0);
198
0
}
199
200
void *
201
ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
202
318k
{
203
  /* only allowed to seek in already written parts */
204
318k
  if (len > SIZE_MAX - pos || pos + len > buf->wpos) {
205
4.90k
    errno = ERANGE;
206
4.90k
    return (NULL);
207
4.90k
  }
208
209
313k
  return (buf->buf + pos);
210
318k
}
211
212
int
213
ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len)
214
0
{
215
0
  void *b;
216
217
0
  if ((b = ibuf_seek(buf, pos, len)) == NULL)
218
0
    return (-1);
219
220
0
  memcpy(b, data, len);
221
0
  return (0);
222
0
}
223
224
int
225
ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value)
226
0
{
227
0
  uint8_t v;
228
229
0
  if (value > UINT8_MAX) {
230
0
    errno = EINVAL;
231
0
    return (-1);
232
0
  }
233
0
  v = value;
234
0
  return (ibuf_set(buf, pos, &v, sizeof(v)));
235
0
}
236
237
int
238
ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value)
239
0
{
240
0
  uint16_t v;
241
242
0
  if (value > UINT16_MAX) {
243
0
    errno = EINVAL;
244
0
    return (-1);
245
0
  }
246
0
  v = htobe16(value);
247
0
  return (ibuf_set(buf, pos, &v, sizeof(v)));
248
0
}
249
250
int
251
ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value)
252
0
{
253
0
  uint32_t v;
254
255
0
  if (value > UINT32_MAX) {
256
0
    errno = EINVAL;
257
0
    return (-1);
258
0
  }
259
0
  v = htobe32(value);
260
0
  return (ibuf_set(buf, pos, &v, sizeof(v)));
261
0
}
262
263
int
264
ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value)
265
0
{
266
0
  value = htobe64(value);
267
0
  return (ibuf_set(buf, pos, &value, sizeof(value)));
268
0
}
269
270
void *
271
ibuf_data(struct ibuf *buf)
272
1.00M
{
273
1.00M
  return (buf->buf);
274
1.00M
}
275
276
size_t
277
ibuf_size(struct ibuf *buf)
278
16.0k
{
279
16.0k
  return (buf->wpos);
280
16.0k
}
281
282
size_t
283
ibuf_left(struct ibuf *buf)
284
0
{
285
0
  return (buf->max - buf->wpos);
286
0
}
287
288
void
289
ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
290
0
{
291
0
  ibuf_enqueue(msgbuf, buf);
292
0
}
293
294
void
295
ibuf_free(struct ibuf *buf)
296
218k
{
297
218k
  if (buf == NULL)
298
191k
    return;
299
#ifdef NOTYET
300
  if (buf->fd != -1)
301
    close(buf->fd);
302
#endif
303
26.9k
  freezero(buf->buf, buf->size);
304
26.9k
  free(buf);
305
26.9k
}
306
307
int
308
ibuf_fd_avail(struct ibuf *buf)
309
0
{
310
0
  return (buf->fd != -1);
311
0
}
312
313
int
314
ibuf_fd_get(struct ibuf *buf)
315
0
{
316
0
  int fd;
317
318
0
  fd = buf->fd;
319
#ifdef NOTYET
320
  buf->fd = -1;
321
#endif
322
0
  return (fd);
323
0
}
324
325
void
326
ibuf_fd_set(struct ibuf *buf, int fd)
327
0
{
328
0
  if (buf->fd != -1)
329
0
    close(buf->fd);
330
0
  buf->fd = fd;
331
0
}
332
333
#ifdef _WIN32
334
#define IBUF_WSABUF_WRITE_MAX 16
335
int
336
ibuf_write(struct msgbuf *msgbuf)
337
{
338
  DWORD    bytesSent;
339
  WSABUF     iov[IBUF_WSABUF_WRITE_MAX];
340
  struct ibuf *buf;
341
  unsigned int   i = 0;
342
  ssize_t n;
343
344
  memset(&iov, 0, sizeof(iov));
345
  TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
346
    if (i >= IBUF_WSABUF_WRITE_MAX)
347
      break;
348
    iov[i].buf = buf->buf + buf->rpos;
349
    iov[i].len = buf->wpos - buf->rpos;
350
    i++;
351
  }
352
353
  if (WSASend(msgbuf->fd, iov, i, &bytesSent, 0, NULL, NULL))
354
    return (-1);
355
  n = bytesSent;
356
357
  if (n == 0) {     /* connection closed */
358
    errno = 0;
359
    return (0);
360
  }
361
362
  msgbuf_drain(msgbuf, n);
363
364
  return (1);
365
}
366
#else
367
int
368
ibuf_write(struct msgbuf *msgbuf)
369
0
{
370
0
  struct iovec   iov[IOV_MAX];
371
0
  struct ibuf *buf;
372
0
  unsigned int   i = 0;
373
0
  ssize_t n;
374
375
0
  memset(&iov, 0, sizeof(iov));
376
0
  TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
377
0
    if (i >= IOV_MAX)
378
0
      break;
379
0
    iov[i].iov_base = buf->buf + buf->rpos;
380
0
    iov[i].iov_len = buf->wpos - buf->rpos;
381
0
    i++;
382
0
  }
383
384
0
again:
385
0
  if ((n = writev(msgbuf->fd, iov, i)) == -1) {
386
0
    if (errno == EINTR)
387
0
      goto again;
388
0
    if (errno == ENOBUFS)
389
0
      errno = EAGAIN;
390
0
    return (-1);
391
0
  }
392
393
0
  if (n == 0) {     /* connection closed */
394
0
    errno = 0;
395
0
    return (0);
396
0
  }
397
398
0
  msgbuf_drain(msgbuf, n);
399
400
0
  return (1);
401
0
}
402
#endif /* !_WIN32 */
403
404
void
405
msgbuf_init(struct msgbuf *msgbuf)
406
0
{
407
0
  msgbuf->queued = 0;
408
0
  msgbuf->fd = -1;
409
0
  TAILQ_INIT(&msgbuf->bufs);
410
0
}
411
412
static void
413
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
414
0
{
415
0
  struct ibuf *buf, *next;
416
417
0
  for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
418
0
      buf = next) {
419
0
    next = TAILQ_NEXT(buf, entry);
420
0
    if (n >= buf->wpos - buf->rpos) {
421
0
      n -= buf->wpos - buf->rpos;
422
0
      ibuf_dequeue(msgbuf, buf);
423
0
    } else {
424
0
      buf->rpos += n;
425
0
      n = 0;
426
0
    }
427
0
  }
428
0
}
429
430
void
431
msgbuf_clear(struct msgbuf *msgbuf)
432
0
{
433
0
  struct ibuf *buf;
434
435
0
  while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
436
0
    ibuf_dequeue(msgbuf, buf);
437
0
}
438
439
#ifdef _WIN32
440
int
441
msgbuf_write(struct msgbuf *msgbuf)
442
{
443
  struct ibuf *buf;
444
445
  TAILQ_FOREACH(buf, &msgbuf->bufs, entry)
446
    if (buf->fd != -1)
447
      return (-1);
448
  return ibuf_write(msgbuf);
449
}
450
#else
451
int
452
msgbuf_write(struct msgbuf *msgbuf)
453
0
{
454
0
  struct iovec   iov[IOV_MAX];
455
0
  struct ibuf *buf, *buf0 = NULL;
456
0
  unsigned int   i = 0;
457
0
  ssize_t    n;
458
0
  struct msghdr  msg;
459
0
  struct cmsghdr  *cmsg;
460
0
  union {
461
0
    struct cmsghdr  hdr;
462
0
    char    buf[CMSG_SPACE(sizeof(int))];
463
0
  } cmsgbuf;
464
465
0
  memset(&iov, 0, sizeof(iov));
466
0
  memset(&msg, 0, sizeof(msg));
467
0
  memset(&cmsgbuf, 0, sizeof(cmsgbuf));
468
0
  TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
469
0
    if (i >= IOV_MAX)
470
0
      break;
471
0
    if (i > 0 && buf->fd != -1)
472
0
      break;
473
0
    iov[i].iov_base = buf->buf + buf->rpos;
474
0
    iov[i].iov_len = buf->wpos - buf->rpos;
475
0
    i++;
476
0
    if (buf->fd != -1)
477
0
      buf0 = buf;
478
0
  }
479
480
0
  msg.msg_iov = iov;
481
0
  msg.msg_iovlen = i;
482
483
0
  if (buf0 != NULL) {
484
0
    msg.msg_control = (caddr_t)&cmsgbuf.buf;
485
0
    msg.msg_controllen = sizeof(cmsgbuf.buf);
486
0
    cmsg = CMSG_FIRSTHDR(&msg);
487
0
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
488
0
    cmsg->cmsg_level = SOL_SOCKET;
489
0
    cmsg->cmsg_type = SCM_RIGHTS;
490
0
    *(int *)CMSG_DATA(cmsg) = buf0->fd;
491
0
  }
492
493
0
again:
494
0
  if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
495
0
    if (errno == EINTR)
496
0
      goto again;
497
0
    if (errno == ENOBUFS)
498
0
      errno = EAGAIN;
499
0
    return (-1);
500
0
  }
501
502
0
  if (n == 0) {     /* connection closed */
503
0
    errno = 0;
504
0
    return (0);
505
0
  }
506
507
  /*
508
   * assumption: fd got sent if sendmsg sent anything
509
   * this works because fds are passed one at a time
510
   */
511
0
  if (buf0 != NULL) {
512
0
    close(buf0->fd);
513
0
    buf0->fd = -1;
514
0
  }
515
516
0
  msgbuf_drain(msgbuf, n);
517
518
0
  return (1);
519
0
}
520
#endif /* !_WIN32 */
521
522
static void
523
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
524
0
{
525
0
  TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
526
0
  msgbuf->queued++;
527
0
}
528
529
static void
530
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
531
0
{
532
0
  TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
533
534
0
  if (buf->fd != -1) {
535
0
    close(buf->fd);
536
0
    buf->fd = -1;
537
0
  }
538
539
0
  msgbuf->queued--;
540
0
  ibuf_free(buf);
541
0
}