Coverage Report

Created: 2023-06-07 06:09

/src/varnish-cache/bin/varnishd/cache/cache_ws_emu.c
Line
Count
Source (jump to first uncovered line)
1
/*-
2
 * Copyright (c) 2021 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 */
31
32
#include "config.h"
33
34
#ifdef ENABLE_WORKSPACE_EMULATOR
35
36
#if HAVE_SANITIZER_ASAN_INTERFACE_H
37
#  include <sanitizer/asan_interface.h>
38
#endif
39
40
#include "cache_varnishd.h"
41
42
#include <stdlib.h>
43
44
struct ws_alloc {
45
  unsigned    magic;
46
#define WS_ALLOC_MAGIC    0x22e7fd05
47
  unsigned    off;
48
  unsigned    len;
49
  char      *ptr;
50
  VTAILQ_ENTRY(ws_alloc)  list;
51
};
52
53
VTAILQ_HEAD(ws_alloc_head, ws_alloc);
54
55
struct ws_emu {
56
  unsigned    magic;
57
#define WS_EMU_MAGIC    0x1c89b6ab
58
  unsigned    len;
59
  struct ws   *ws;
60
  struct ws_alloc_head  head;
61
};
62
63
static const uintptr_t snap_overflowed = (uintptr_t)&snap_overflowed;
64
65
static struct ws_emu *
66
ws_emu(const struct ws *ws)
67
6.32k
{
68
6.32k
  struct ws_emu *we;
69
70
6.32k
  CAST_OBJ_NOTNULL(we, (void *)ws->s, WS_EMU_MAGIC);
71
6.32k
  return (we);
72
6.32k
}
73
74
void
75
WS_Assert(const struct ws *ws)
76
4.74k
{
77
4.74k
  struct ws_emu *we;
78
4.74k
  struct ws_alloc *wa, *wa2 = NULL;
79
4.74k
  size_t len;
80
81
4.74k
  CHECK_OBJ_NOTNULL(ws, WS_MAGIC);
82
4.74k
  assert(ws->s != NULL);
83
4.74k
  assert(PAOK(ws->s));
84
4.74k
  assert(ws->e != NULL);
85
4.74k
  assert(PAOK(ws->e));
86
87
4.74k
  we = ws_emu(ws);
88
4.74k
  len = pdiff(ws->s, ws->e);
89
4.74k
  assert(len == we->len);
90
91
4.74k
  len = 0;
92
4.74k
  VTAILQ_FOREACH(wa, &we->head, list) {
93
2.37k
    CHECK_OBJ_NOTNULL(wa, WS_ALLOC_MAGIC);
94
2.37k
    wa2 = wa;
95
2.37k
    assert(len == wa->off);
96
2.37k
    if (wa->ptr == ws->f || wa->ptr == NULL) /* reservation */
97
0
      break;
98
2.37k
    AN(wa->len);
99
2.37k
    len += PRNDUP(wa->len);
100
2.37k
    assert(len <= we->len);
101
2.37k
  }
102
103
4.74k
  if (wa != NULL) {
104
0
    AZ(VTAILQ_NEXT(wa, list));
105
0
    if (wa->ptr == NULL) {
106
0
      AZ(wa->len);
107
0
      assert(ws->f == ws->e);
108
0
      assert(ws->r == ws->e);
109
0
    } else {
110
0
      AN(wa->len);
111
0
      assert(ws->f == wa->ptr);
112
0
      assert(ws->r == ws->f + wa->len);
113
0
    }
114
0
    len += PRNDUP(wa->len);
115
0
    assert(len <= we->len);
116
4.74k
  } else {
117
4.74k
    AZ(ws->f);
118
4.74k
    AZ(ws->r);
119
4.74k
  }
120
121
4.74k
  DSLb(DBG_WORKSPACE, "WS(%p) = (%s, %p %zu %zu %zu)",
122
4.74k
      ws, ws->id, ws->s, wa2 == NULL ? 0 : wa2->off + PRNDUP(wa2->len),
123
4.74k
      ws->r == NULL ? 0 : pdiff(ws->f, ws->r),
124
4.74k
      pdiff(ws->s, ws->e));
125
4.74k
}
126
127
int
128
WS_Allocated(const struct ws *ws, const void *ptr, ssize_t len)
129
0
{
130
0
  struct ws_emu *we;
131
0
  struct ws_alloc *wa;
132
0
  uintptr_t p, pa;
133
134
0
  WS_Assert(ws);
135
0
  AN(ptr);
136
0
  if (len < 0)
137
0
    len = strlen(ptr) + 1;
138
0
  p = (uintptr_t)ptr;
139
0
  we = ws_emu(ws);
140
141
0
  VTAILQ_FOREACH(wa, &we->head, list) {
142
0
    pa = (uintptr_t)wa->ptr;
143
0
    if (p >= (uintptr_t)ws->f && p <= (uintptr_t)ws->r)
144
0
      return (1);
145
    /* XXX: clang 12's ubsan triggers a pointer overflow on
146
     * the if statement below. Since the purpose is to check
147
     * that a pointer+length is within bounds of another
148
     * pointer+length it's unclear whether a pointer overflow
149
     * is relevant. Worked around for now with uintptr_t.
150
     */
151
0
    if (p >= pa && p + len <= pa + wa->len)
152
0
      return (1);
153
0
  }
154
0
  return (0);
155
0
}
156
157
void
158
WS_Init(struct ws *ws, const char *id, void *space, unsigned len)
159
790
{
160
790
  struct ws_emu *we;
161
162
790
  DSLb(DBG_WORKSPACE,
163
790
      "WS_Init(%p, \"%s\", %p, %u)", ws, id, space, len);
164
790
  assert(space != NULL);
165
790
  assert(PAOK(space));
166
790
  assert(len >= sizeof *we);
167
168
790
  len = PRNDDN(len - 1);
169
790
  INIT_OBJ(ws, WS_MAGIC);
170
790
  ws->s = space;
171
790
  ws->e = ws->s + len;
172
173
790
  assert(id[0] & 0x20);   // cheesy islower()
174
790
  bstrcpy(ws->id, id);
175
176
790
  we = space;
177
790
  INIT_OBJ(we, WS_EMU_MAGIC);
178
790
  VTAILQ_INIT(&we->head);
179
790
  we->len = len;
180
181
790
  WS_Assert(ws);
182
790
}
183
184
static void
185
ws_alloc_free(struct ws_emu *we, struct ws_alloc **wap)
186
790
{
187
790
  struct ws_alloc *wa;
188
189
790
  TAKE_OBJ_NOTNULL(wa, wap, WS_ALLOC_MAGIC);
190
790
  AZ(VTAILQ_NEXT(wa, list));
191
790
  VTAILQ_REMOVE(&we->head, wa, list);
192
790
  free(wa->ptr);
193
790
  FREE_OBJ(wa);
194
790
}
195
196
void
197
WS_Reset(struct ws *ws, uintptr_t pp)
198
790
{
199
790
  struct ws_emu *we;
200
790
  struct ws_alloc *wa;
201
790
  char *p;
202
203
790
  WS_Assert(ws);
204
790
  AN(pp);
205
790
  if (pp == snap_overflowed) {
206
0
    DSLb(DBG_WORKSPACE, "WS_Reset(%p, overflowed)", ws);
207
0
    AN(WS_Overflowed(ws));
208
0
    return;
209
0
  }
210
790
  p = (char *)pp;
211
790
  DSLb(DBG_WORKSPACE, "WS_Reset(%p, %p)", ws, p);
212
790
  AZ(ws->r);
213
214
790
  we = ws_emu(ws);
215
1.58k
  while ((wa = VTAILQ_LAST(&we->head, ws_alloc_head)) != NULL &&
216
1.58k
      wa->ptr != p)
217
790
    ws_alloc_free(we, &wa);
218
790
  if (wa == NULL)
219
790
    assert(p == ws->s);
220
221
790
  WS_Assert(ws);
222
790
}
223
224
unsigned
225
WS_ReqPipeline(struct ws *ws, const void *b, const void *e)
226
0
{
227
0
  struct ws_emu *we;
228
0
  struct ws_alloc *wa;
229
0
  unsigned l;
230
231
0
  WS_Assert(ws);
232
0
  AZ(ws->f);
233
0
  AZ(ws->r);
234
235
0
  if (strcasecmp(ws->id, "req"))
236
0
    AZ(b);
237
238
0
  if (b == NULL) {
239
0
    AZ(e);
240
0
    if (!strcasecmp(ws->id, "req"))
241
0
      WS_Rollback(ws, 0);
242
0
    (void)WS_ReserveAll(ws);
243
0
    return (0);
244
0
  }
245
246
0
  we = ws_emu(ws);
247
0
  ALLOC_OBJ(wa, WS_ALLOC_MAGIC);
248
0
  AN(wa);
249
0
  wa->len = we->len;
250
0
  wa->ptr = malloc(wa->len);
251
0
  AN(wa->ptr);
252
253
0
  AN(e);
254
0
  l = pdiff(b, e);
255
0
  assert(l <= wa->len);
256
0
  memcpy(wa->ptr, b, l);
257
258
0
  WS_Rollback(ws, 0);
259
0
  ws->f = wa->ptr;
260
0
  ws->r = ws->f + wa->len;
261
0
  VTAILQ_INSERT_TAIL(&we->head, wa, list);
262
0
  WS_Assert(ws);
263
0
  return (l);
264
0
}
265
266
static struct ws_alloc *
267
ws_emu_alloc(struct ws *ws, unsigned len)
268
790
{
269
790
  struct ws_emu *we;
270
790
  struct ws_alloc *wa;
271
790
  size_t off = 0;
272
273
790
  WS_Assert(ws);
274
790
  AZ(ws->r);
275
276
790
  we = ws_emu(ws);
277
790
  wa = VTAILQ_LAST(&we->head, ws_alloc_head);
278
790
  CHECK_OBJ_ORNULL(wa, WS_ALLOC_MAGIC);
279
280
790
  if (wa != NULL)
281
0
    off = wa->off + PRNDUP(wa->len);
282
790
  if (off + len > we->len) {
283
0
    WS_MarkOverflow(ws);
284
0
    return (NULL);
285
0
  }
286
790
  if (len == 0)
287
0
    len = we->len - off;
288
289
790
  ALLOC_OBJ(wa, WS_ALLOC_MAGIC);
290
790
  AN(wa);
291
790
  wa->off = off;
292
790
  wa->len = len;
293
790
  if (len > 0) {
294
790
    wa->ptr = malloc(len);
295
790
    AN(wa->ptr);
296
790
  }
297
790
  VTAILQ_INSERT_TAIL(&we->head, wa, list);
298
790
  return (wa);
299
790
}
300
301
void *
302
WS_Alloc(struct ws *ws, unsigned bytes)
303
790
{
304
790
  struct ws_alloc *wa;
305
306
790
  assert(bytes > 0);
307
790
  wa = ws_emu_alloc(ws, bytes);
308
790
  WS_Assert(ws);
309
790
  if (wa != NULL) {
310
790
    AN(wa->ptr);
311
790
    DSLb(DBG_WORKSPACE, "WS_Alloc(%p, %u) = %p",
312
790
        ws, bytes, wa->ptr);
313
790
    return (wa->ptr);
314
790
  }
315
0
  return (NULL);
316
790
}
317
318
void *
319
WS_Copy(struct ws *ws, const void *str, int len)
320
0
{
321
0
  struct ws_alloc *wa;
322
323
0
  AN(str);
324
0
  if (len == -1)
325
0
    len = strlen(str) + 1;
326
0
  assert(len > 0);
327
0
  wa = ws_emu_alloc(ws, len);
328
0
  WS_Assert(ws);
329
0
  if (wa != NULL) {
330
0
    AN(wa->ptr);
331
0
    memcpy(wa->ptr, str, len);
332
0
    DSLb(DBG_WORKSPACE, "WS_Copy(%p, %d) = %p",
333
0
        ws, len, wa->ptr);
334
0
    return (wa->ptr);
335
0
  }
336
0
  return (NULL);
337
0
}
338
339
uintptr_t
340
WS_Snapshot(struct ws *ws)
341
0
{
342
0
  struct ws_emu *we;
343
0
  struct ws_alloc *wa;
344
0
  void *p;
345
346
0
  WS_Assert(ws);
347
0
  assert(ws->r == NULL);
348
0
  if (WS_Overflowed(ws)) {
349
0
    DSLb(DBG_WORKSPACE, "WS_Snapshot(%p) = overflowed", ws);
350
0
    return (snap_overflowed);
351
0
  }
352
353
0
  we = ws_emu(ws);
354
0
  wa = VTAILQ_LAST(&we->head, ws_alloc_head);
355
0
  CHECK_OBJ_ORNULL(wa, WS_ALLOC_MAGIC);
356
0
  p = (wa == NULL ? ws->s : wa->ptr);
357
0
  DSLb(DBG_WORKSPACE, "WS_Snapshot(%p) = %p", ws, p);
358
0
  return ((uintptr_t)p);
359
0
}
360
361
unsigned
362
WS_ReserveAll(struct ws *ws)
363
0
{
364
0
  struct ws_alloc *wa;
365
0
  unsigned b;
366
367
0
  wa = ws_emu_alloc(ws, 0);
368
0
  AN(wa);
369
370
0
  if (wa->ptr != NULL) {
371
0
    AN(wa->len);
372
0
    ws->f = wa->ptr;
373
0
    ws->r = ws->f + wa->len;
374
0
  } else {
375
0
    ws->f = ws->r = ws->e;
376
0
  }
377
378
0
  b = pdiff(ws->f, ws->r);
379
0
  DSLb(DBG_WORKSPACE, "WS_ReserveAll(%p) = %u", ws, b);
380
0
  WS_Assert(ws);
381
0
  return (b);
382
0
}
383
384
unsigned
385
WS_ReserveSize(struct ws *ws, unsigned bytes)
386
0
{
387
0
  struct ws_emu *we;
388
0
  struct ws_alloc *wa;
389
390
0
  assert(bytes > 0);
391
0
  wa = ws_emu_alloc(ws, bytes);
392
0
  if (wa == NULL)
393
0
    return (0);
394
395
0
  AN(wa->ptr);
396
0
  assert(wa->len == bytes);
397
0
  ws->f = wa->ptr;
398
0
  ws->r = ws->f + bytes;
399
0
  we = ws_emu(ws);
400
0
  DSLb(DBG_WORKSPACE, "WS_ReserveSize(%p, %u/%u) = %u",
401
0
      ws, bytes, we->len - wa->off, bytes);
402
0
  WS_Assert(ws);
403
0
  return (bytes);
404
0
}
405
406
static void
407
ws_release(struct ws *ws, unsigned bytes)
408
0
{
409
0
  struct ws_emu *we;
410
0
  struct ws_alloc *wa;
411
412
0
  WS_Assert(ws);
413
0
  AN(ws->f);
414
0
  AN(ws->r);
415
0
  we = ws_emu(ws);
416
0
  wa = VTAILQ_LAST(&we->head, ws_alloc_head);
417
0
  AN(wa);
418
0
  assert(bytes <= wa->len);
419
0
  ws->f = ws->r = NULL;
420
421
0
  if (bytes == 0) {
422
0
    ws_alloc_free(we, &wa);
423
0
    return;
424
0
  }
425
426
0
  AN(wa->ptr);
427
#ifdef ASAN_POISON_MEMORY_REGION
428
  ASAN_POISON_MEMORY_REGION(wa->ptr + bytes, wa->len - bytes);
429
#endif
430
0
  wa->len = bytes;
431
0
  WS_Assert(ws);
432
0
}
433
434
void
435
WS_Release(struct ws *ws, unsigned bytes)
436
0
{
437
438
0
  ws_release(ws, bytes);
439
0
  DSLb(DBG_WORKSPACE, "WS_Release(%p, %u)", ws, bytes);
440
0
}
441
442
void
443
WS_ReleaseP(struct ws *ws, const char *ptr)
444
0
{
445
0
  unsigned l;
446
447
0
  WS_Assert(ws);
448
0
  assert(ws->r != NULL);
449
0
  assert(ptr >= ws->f);
450
0
  assert(ptr <= ws->r);
451
0
  l = pdiff(ws->f, ptr);
452
0
  ws_release(ws, l);
453
0
  DSLb(DBG_WORKSPACE, "WS_ReleaseP(%p, %p (%u))", ws, ptr, l);
454
0
}
455
456
void *
457
WS_AtOffset(const struct ws *ws, unsigned off, unsigned len)
458
0
{
459
0
  struct ws_emu *we;
460
0
  struct ws_alloc *wa;
461
462
0
  WS_Assert(ws);
463
0
  we = ws_emu(ws);
464
465
0
  VTAILQ_FOREACH(wa, &we->head, list) {
466
0
    if (wa->off == off) {
467
0
      assert(wa->len >= len);
468
0
      return (wa->ptr);
469
0
    }
470
0
  }
471
472
0
  WRONG("invalid offset");
473
0
  NEEDLESS(return (NULL));
474
0
}
475
476
unsigned
477
WS_ReservationOffset(const struct ws *ws)
478
0
{
479
0
  struct ws_emu *we;
480
0
  struct ws_alloc *wa;
481
482
0
  WS_Assert(ws);
483
0
  AN(ws->f);
484
0
  AN(ws->r);
485
0
  we = ws_emu(ws);
486
0
  wa = VTAILQ_LAST(&we->head, ws_alloc_head);
487
0
  AN(wa);
488
0
  return (wa->off);
489
0
}
490
491
unsigned
492
WS_Dump(const struct ws *ws, char where, size_t off, void *buf, size_t len)
493
0
{
494
0
  struct ws_emu *we;
495
0
  struct ws_alloc *wa;
496
0
  unsigned l;
497
0
  char *b;
498
499
0
  WS_Assert(ws);
500
0
  AN(buf);
501
0
  AN(len);
502
503
0
  if (strchr("sfr", where) == NULL) {
504
0
    errno = EINVAL;
505
0
    return (0);
506
0
  }
507
508
0
  if (where == 'r' && ws->r == NULL) {
509
0
    errno = EAGAIN;
510
0
    return (0);
511
0
  }
512
513
0
  we = ws_emu(ws);
514
0
  wa = VTAILQ_LAST(&we->head, ws_alloc_head);
515
516
0
  l = we->len;
517
0
  if (where != 's' && wa != NULL) {
518
0
    l -= wa->off;
519
0
    if (where == 'f')
520
0
      l -= wa->len;
521
0
  }
522
523
0
  if (off > l) {
524
0
    errno = EFAULT;
525
0
    return (0);
526
0
  }
527
528
0
  b = buf;
529
0
  if (where == 'f' && ws->r != NULL) {
530
0
    if (l > len)
531
0
      l = len;
532
0
    AN(wa);
533
0
    memcpy(b, wa->ptr, l);
534
0
    b += l;
535
0
    len -= l;
536
0
  }
537
538
0
  if (where == 's') {
539
0
    VTAILQ_FOREACH(wa, &we->head, list) {
540
0
      if (len == 0)
541
0
        break;
542
0
      if (wa->ptr == NULL)
543
0
        break;
544
0
      l = vmin_t(size_t, wa->len, len);
545
0
      memcpy(b, wa->ptr, l);
546
0
      b += l;
547
0
      len -= l;
548
0
    }
549
0
  }
550
551
0
  if (len > 0)
552
0
    memset(b, 0xa5, len);
553
0
  return (l);
554
0
}
555
556
static void
557
ws_emu_panic(struct vsb *vsb, const struct ws *ws)
558
0
{
559
0
  const struct ws_emu *we;
560
0
  const struct ws_alloc *wa;
561
562
0
  we = (void *)ws->s;
563
0
  if (PAN_dump_once(vsb, we, WS_EMU_MAGIC, "ws_emu"))
564
0
    return;
565
0
  VSB_printf(vsb, "len = %u,\n", we->len);
566
567
0
  VTAILQ_FOREACH(wa, &we->head, list) {
568
0
    if (PAN_dump_once_oneline(vsb, wa, WS_ALLOC_MAGIC, "ws_alloc"))
569
0
      break;
570
0
    VSB_printf(vsb, "off, len, ptr} = {%u, %u, %p}\n",
571
0
        wa->off, wa->len, wa->ptr);
572
0
  }
573
574
0
  VSB_indent(vsb, -2);
575
0
  VSB_cat(vsb, "},\n");
576
0
}
577
578
void
579
WS_Panic(struct vsb *vsb, const struct ws *ws)
580
0
{
581
582
0
  if (PAN_dump_struct(vsb, ws, WS_MAGIC, "ws"))
583
0
    return;
584
0
  if (ws->id[0] != '\0' && (!(ws->id[0] & 0x20))) // cheesy islower()
585
0
    VSB_cat(vsb, "OVERFLOWED ");
586
0
  VSB_printf(vsb, "id = \"%s\",\n", ws->id);
587
0
  VSB_printf(vsb, "{s, e} = {%p", ws->s);
588
0
  if (ws->e >= ws->s)
589
0
    VSB_printf(vsb, ", +%ld", (long) (ws->e - ws->s));
590
0
  else
591
0
    VSB_printf(vsb, ", %p", ws->e);
592
0
  VSB_cat(vsb, "},\n");
593
0
  VSB_printf(vsb, "{f, r} = {%p", ws->f);
594
0
  if (ws->r >= ws->f)
595
0
    VSB_printf(vsb, ", +%ld", (long) (ws->r - ws->f));
596
0
  else
597
0
    VSB_printf(vsb, ", %p", ws->r);
598
0
  VSB_cat(vsb, "},\n");
599
600
0
  ws_emu_panic(vsb, ws);
601
602
0
  VSB_indent(vsb, -2);
603
0
  VSB_cat(vsb, "},\n");
604
0
}
605
606
#endif /* ENABLE_WORKSPACE_EMULATOR */