Coverage Report

Created: 2026-11-12 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openiked-portable/compat/imsg.c
Line
Count
Source
1
/*  $OpenBSD: imsg.c,v 1.43 2026/03/02 20:07:58 claudio Exp $ */
2
3
/*
4
 * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
5
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
#include <sys/types.h>
21
#include <sys/queue.h>
22
#include <sys/socket.h>
23
#include <sys/uio.h>
24
25
#include <errno.h>
26
#include <stddef.h>
27
#include <stdint.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <unistd.h>
31
32
#include "openbsd-compat.h"
33
#include "imsg.h"
34
35
0
#define IMSG_ALLOW_FDPASS 0x01
36
0
#define IMSG_FD_MARK    0x80000000U
37
38
static struct ibuf  *imsg_parse_hdr(struct ibuf *, void *, int *);
39
40
int
41
imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
42
0
{
43
0
  imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
44
0
      imsgbuf);
45
0
  if (imsgbuf->w == NULL)
46
0
    return (-1);
47
0
  imsgbuf->pid = getpid();
48
0
  imsgbuf->maxsize = MAX_IMSGSIZE;
49
0
  imsgbuf->fd = fd;
50
0
  imsgbuf->flags = 0;
51
0
  return (0);
52
0
}
53
54
void
55
imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
56
0
{
57
0
  imsgbuf->flags |= IMSG_ALLOW_FDPASS;
58
0
}
59
60
int
61
imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t max)
62
0
{
63
0
  if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
64
0
    errno = ERANGE;
65
0
    return (-1);
66
0
  }
67
0
  max += IMSG_HEADER_SIZE;
68
0
  if (max & IMSG_FD_MARK) {
69
0
    errno = EINVAL;
70
0
    return (-1);
71
0
  }
72
0
  imsgbuf->maxsize = max;
73
0
  return (0);
74
0
}
75
76
int
77
imsgbuf_read(struct imsgbuf *imsgbuf)
78
0
{
79
0
  if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
80
0
    return msgbuf_read(imsgbuf->fd, imsgbuf->w);
81
0
  else
82
0
    return ibuf_read(imsgbuf->fd, imsgbuf->w);
83
0
}
84
85
int
86
imsgbuf_write(struct imsgbuf *imsgbuf)
87
0
{
88
0
  if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
89
0
    return msgbuf_write(imsgbuf->fd, imsgbuf->w);
90
0
  else
91
0
    return ibuf_write(imsgbuf->fd, imsgbuf->w);
92
0
}
93
94
int
95
imsgbuf_flush(struct imsgbuf *imsgbuf)
96
0
{
97
0
  while (imsgbuf_queuelen(imsgbuf) > 0) {
98
0
    if (imsgbuf_write(imsgbuf) == -1)
99
0
      return (-1);
100
0
  }
101
0
  return (0);
102
0
}
103
104
void
105
imsgbuf_clear(struct imsgbuf *imsgbuf)
106
0
{
107
0
  msgbuf_free(imsgbuf->w);
108
0
  imsgbuf->w = NULL;
109
0
}
110
111
uint32_t
112
imsgbuf_queuelen(struct imsgbuf *imsgbuf)
113
0
{
114
0
  return msgbuf_queuelen(imsgbuf->w);
115
0
}
116
117
int
118
imsgbuf_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
119
0
{
120
0
  struct imsg  m;
121
0
  struct ibuf *buf;
122
123
0
  if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
124
0
    return (0);
125
126
0
  if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
127
0
    return (-1);
128
129
0
  if (ibuf_size(buf))
130
0
    m.data = ibuf_data(buf);
131
0
  else
132
0
    m.data = NULL;
133
0
  m.buf = buf;
134
0
  m.hdr.len &= ~IMSG_FD_MARK;
135
136
0
  *imsg = m;
137
0
  return (1);
138
0
}
139
140
ssize_t
141
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
142
0
{
143
0
  int rv;
144
145
0
  if ((rv = imsgbuf_get(imsgbuf, imsg)) != 1)
146
0
    return rv;
147
0
  return (imsg_get_len(imsg) + IMSG_HEADER_SIZE);
148
0
}
149
150
int
151
imsg_ibufq_pop(struct ibufqueue *bufq, struct imsg *imsg)
152
0
{
153
0
  struct imsg  m;
154
0
  struct ibuf *buf;
155
156
0
  if ((buf = ibufq_pop(bufq)) == NULL)
157
0
    return (0);
158
159
0
  if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
160
0
    return (-1);
161
162
0
  if (ibuf_size(buf))
163
0
    m.data = ibuf_data(buf);
164
0
  else
165
0
    m.data = NULL;
166
0
  m.buf = buf;
167
0
  m.hdr.len &= ~IMSG_FD_MARK;
168
169
0
  *imsg = m;
170
0
  return (1);
171
0
}
172
173
void
174
imsg_ibufq_push(struct ibufqueue *bufq, struct imsg *imsg)
175
0
{
176
0
  ibuf_rewind(imsg->buf);
177
0
  ibufq_push(bufq, imsg->buf);
178
0
  memset(imsg, 0, sizeof(*imsg));
179
0
}
180
181
int
182
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
183
0
{
184
0
  if (ibuf_size(imsg->buf) == 0) {
185
0
    errno = EBADMSG;
186
0
    return (-1);
187
0
  }
188
0
  return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
189
0
}
190
191
int
192
imsg_get_data(struct imsg *imsg, void *data, size_t len)
193
0
{
194
0
  if (len == 0) {
195
0
    errno = EINVAL;
196
0
    return (-1);
197
0
  }
198
0
  if (ibuf_size(imsg->buf) != len) {
199
0
    errno = EBADMSG;
200
0
    return (-1);
201
0
  }
202
0
  return ibuf_get(imsg->buf, data, len);
203
0
}
204
205
int
206
imsg_get_buf(struct imsg *imsg, void *data, size_t len)
207
0
{
208
0
  return ibuf_get(imsg->buf, data, len);
209
0
}
210
211
int
212
imsg_get_strbuf(struct imsg *imsg, char *str, size_t len)
213
0
{
214
0
  return ibuf_get_strbuf(imsg->buf, str, len);
215
0
}
216
217
int
218
imsg_get_fd(struct imsg *imsg)
219
0
{
220
0
  return ibuf_fd_get(imsg->buf);
221
0
}
222
223
uint32_t
224
imsg_get_id(struct imsg *imsg)
225
0
{
226
0
  return (imsg->hdr.peerid);
227
0
}
228
229
size_t
230
imsg_get_len(struct imsg *imsg)
231
0
{
232
0
  return ibuf_size(imsg->buf);
233
0
}
234
235
pid_t
236
imsg_get_pid(struct imsg *imsg)
237
0
{
238
0
  return (imsg->hdr.pid);
239
0
}
240
241
uint32_t
242
imsg_get_type(struct imsg *imsg)
243
0
{
244
0
  return (imsg->hdr.type);
245
0
}
246
247
int
248
imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
249
    int fd, const void *data, size_t datalen)
250
0
{
251
0
  struct ibuf *wbuf;
252
253
0
  if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
254
0
    goto fail;
255
256
0
  if (ibuf_add(wbuf, data, datalen) == -1)
257
0
    goto fail;
258
259
0
  ibuf_fd_set(wbuf, fd);
260
0
  imsg_close(imsgbuf, wbuf);
261
262
0
  return (1);
263
264
0
 fail:
265
0
  ibuf_free(wbuf);
266
0
  return (-1);
267
0
}
268
269
int
270
imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
271
    int fd, const struct iovec *iov, int iovcnt)
272
0
{
273
0
  struct ibuf *wbuf;
274
0
  int    i;
275
0
  size_t     datalen = 0;
276
277
0
  for (i = 0; i < iovcnt; i++)
278
0
    datalen += iov[i].iov_len;
279
280
0
  if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
281
0
    goto fail;
282
283
0
  for (i = 0; i < iovcnt; i++)
284
0
    if (ibuf_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
285
0
      goto fail;
286
287
0
  ibuf_fd_set(wbuf, fd);
288
0
  imsg_close(imsgbuf, wbuf);
289
290
0
  return (1);
291
292
0
 fail:
293
0
  ibuf_free(wbuf);
294
0
  return (-1);
295
0
}
296
297
/*
298
 * Enqueue imsg with payload from ibuf buf. fd passing is not possible 
299
 * with this function.
300
 */
301
int
302
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
303
    pid_t pid, struct ibuf *buf)
304
0
{
305
0
  struct ibuf *hdrbuf = NULL;
306
0
  struct imsg_hdr  hdr;
307
308
0
  if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
309
0
    errno = ERANGE;
310
0
    goto fail;
311
0
  }
312
313
0
  hdr.type = type;
314
0
  hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
315
0
  hdr.peerid = id;
316
0
  if ((hdr.pid = pid) == 0)
317
0
    hdr.pid = imsgbuf->pid;
318
319
0
  if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
320
0
    goto fail;
321
0
  if (ibuf_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
322
0
    goto fail;
323
324
0
  ibuf_close(imsgbuf->w, hdrbuf);
325
0
  ibuf_close(imsgbuf->w, buf);
326
0
  return (1);
327
328
0
 fail:
329
0
  ibuf_free(buf);
330
0
  ibuf_free(hdrbuf);
331
0
  return (-1);
332
0
}
333
334
/*
335
 * Forward imsg to another channel. Any attached fd is closed.
336
 */
337
int
338
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
339
0
{
340
0
  struct ibuf *wbuf;
341
0
  size_t     len;
342
343
0
  ibuf_rewind(msg->buf);
344
0
  if (ibuf_skip(msg->buf, sizeof(msg->hdr)) == -1)
345
0
    return (-1);
346
0
  len = ibuf_size(msg->buf);
347
348
0
  if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
349
0
      msg->hdr.pid, len)) == NULL)
350
0
    return (-1);
351
352
0
  if (len != 0) {
353
0
    if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
354
0
      ibuf_free(wbuf);
355
0
      return (-1);
356
0
    }
357
0
  }
358
359
0
  imsg_close(imsgbuf, wbuf);
360
0
  return (1);
361
0
}
362
363
struct ibuf *
364
imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
365
    size_t datalen)
366
0
{
367
0
  struct ibuf *wbuf;
368
0
  struct imsg_hdr  hdr;
369
370
0
  datalen += IMSG_HEADER_SIZE;
371
0
  if (datalen > imsgbuf->maxsize) {
372
0
    errno = ERANGE;
373
0
    return (NULL);
374
0
  }
375
376
0
  hdr.len = 0;
377
0
  hdr.type = type;
378
0
  hdr.peerid = id;
379
0
  if ((hdr.pid = pid) == 0)
380
0
    hdr.pid = imsgbuf->pid;
381
0
  if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL)
382
0
    goto fail;
383
0
  if (ibuf_add(wbuf, &hdr, sizeof(hdr)) == -1)
384
0
    goto fail;
385
386
0
  return (wbuf);
387
388
0
 fail:
389
0
  ibuf_free(wbuf);
390
0
  return (NULL);
391
0
}
392
393
int
394
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
395
0
{
396
0
  if (datalen)
397
0
    if (ibuf_add(msg, data, datalen) == -1) {
398
0
      ibuf_free(msg);
399
0
      return (-1);
400
0
    }
401
0
  return (datalen);
402
0
}
403
404
void
405
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
406
0
{
407
0
  uint32_t len;
408
409
0
  len = ibuf_size(msg);
410
0
  if (ibuf_fd_avail(msg))
411
0
    len |= IMSG_FD_MARK;
412
0
  (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
413
0
  ibuf_close(imsgbuf->w, msg);
414
0
}
415
416
void
417
imsg_free(struct imsg *imsg)
418
0
{
419
0
  ibuf_free(imsg->buf);
420
0
}
421
422
int
423
imsg_set_maxsize(struct ibuf *msg, size_t max)
424
0
{
425
0
  if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
426
0
    errno = ERANGE;
427
0
    return (-1);
428
0
  }
429
0
  return ibuf_set_maxsize(msg, max + IMSG_HEADER_SIZE);
430
0
}
431
432
static struct ibuf *
433
imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
434
0
{
435
0
  struct imsgbuf *imsgbuf = arg;
436
0
  struct imsg_hdr hdr;
437
0
  struct ibuf *b;
438
0
  uint32_t len;
439
440
0
  if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
441
0
    return (NULL);
442
443
0
  len = hdr.len & ~IMSG_FD_MARK;
444
445
0
  if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
446
0
    errno = ERANGE;
447
0
    return (NULL);
448
0
  }
449
0
  if ((b = ibuf_open(len)) == NULL)
450
0
    return (NULL);
451
0
  if (hdr.len & IMSG_FD_MARK) {
452
0
    ibuf_fd_set(b, *fd);
453
0
    *fd = -1;
454
0
  }
455
456
0
  return b;
457
0
}