Coverage Report

Created: 2025-10-13 07:06

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