• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 26127302613

19 May 2026 09:51PM UTC coverage: 93.024% (+0.4%) from 92.635%
26127302613

push

github

51329 of 55178 relevant lines covered (93.02%)

215.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.3
/src/pool.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2026 The ProFTPD Project team
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19
 *
20
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21
 * and other respective copyright holders give permission to link this program
22
 * with OpenSSL, and distribute the resulting executable, without including
23
 * the source code for OpenSSL in the source distribution.
24
 */
25

26
/* Resource allocation code */
27

28
#include "conf.h"
29

30
/* Manage free storage blocks */
31

32
union align {
33
  char *cp;
34
  void (*f)(void);
35
  long l;
36
  FILE *fp;
37
  double d;
38
};
39

40
#define CLICK_SZ (sizeof(union align))
41

42
union block_hdr {
43
  union align a;
44

45
  /* Padding */
46
#if defined(_LP64) || defined(__LP64__)
47
  char pad[32];
48
#endif
49

50
  /* Actual header */
51
  struct {
52
    void *endp;
53
    union block_hdr *next;
54
    void *first_avail;
55
  } h;
56
};
57

58
static union block_hdr *block_freelist = NULL;
59

60
/* Statistics */
61
static unsigned int stat_malloc = 0;        /* incr when malloc required */
62
static unsigned int stat_freehit = 0;        /* incr when freelist used */
63

64
static const char *trace_channel = "pool";
65

66
/* Debug flags */
67
static int debug_flags = 0;
68

69
#ifdef PR_USE_DEVEL
70
static void oom_printf(const char *fmt, ...) {
71
  char buf[PR_TUNABLE_BUFFER_SIZE];
×
72
  va_list msg;
×
73

×
74
  memset(buf, '\0', sizeof(buf));
75

×
76
  va_start(msg, fmt);
77
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
×
78
  va_end(msg);
×
79

×
80
  buf[sizeof(buf)-1] = '\0';
81
  fprintf(stderr, "%s\n", buf);
×
82
}
×
83
#endif /* PR_USE_DEVEL */
×
84

85
/* Lowest level memory allocation functions
86
 */
87

88
static void null_alloc(void) {
89
  pr_log_pri(PR_LOG_ALERT, "Out of memory!");
×
90
#ifdef PR_USE_DEVEL
×
91
  if (debug_flags & PR_POOL_DEBUG_FL_OOM_DUMP_POOLS) {
92
    pr_pool_debug_memory(oom_printf);
×
93
  }
×
94
#endif
95

96
  exit(1);
97
}
×
98

99
static void *smalloc(size_t size) {
100
  void *res;
11,614✔
101

11,614✔
102
  if (size == 0) {
103
    /* Avoid zero-length malloc(); on non-POSIX systems, the behavior is
11,614✔
104
     * not dependable.  And on POSIX systems, malloc(3) might still return
105
     * a "unique pointer" for a zero-length allocation (or NULL).
106
     *
107
     * Either way, a zero-length allocation request here means that someone
108
     * is doing something they should not be doing.
109
     */
110
    null_alloc();
111
  }
×
112

113
  res = malloc(size);
114
  if (res == NULL) {
11,614✔
115
    null_alloc();
11,614✔
116
  }
×
117

118
  return res;
119
}
11,614✔
120

121
/* Grab a completely new block from the system pool.  Relies on malloc()
122
 * to return truly aligned memory.
123
 */
124
static union block_hdr *malloc_block(size_t size) {
125
  union block_hdr *blok =
11,614✔
126
    (union block_hdr *) smalloc(size + sizeof(union block_hdr));
11,614✔
127

11,614✔
128
  blok->h.next = NULL;
129
  blok->h.first_avail = (char *) (blok + 1);
11,614✔
130
  blok->h.endp = size + (char *) blok->h.first_avail;
11,614✔
131

11,614✔
132
  return blok;
133
}
11,614✔
134

135
static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk,
136
    const char *pool_tag) {
16,452✔
137

138
#ifdef PR_USE_DEVEL
139
  /* Debug code */
140

141
  while (free_blk) {
142
    if (free_blk != blok) {
111,733✔
143
      free_blk = free_blk->h.next;
95,281✔
144
      continue;
95,281✔
145
    }
95,281✔
146

147
    pr_log_pri(PR_LOG_WARNING, "fatal: DEBUG: Attempt to free already free "
148
     "block in pool '%s'", pool_tag ? pool_tag : "<unnamed>");
×
149
    exit(1);
150
  }
×
151
#endif /* PR_USE_DEVEL */
152
}
153

16,452✔
154
/* Free a chain of blocks -- _must_ call with alarms blocked. */
155

156
static void free_blocks(union block_hdr *blok, const char *pool_tag) {
157
  /* Puts new blocks at head of block list, point next pointer of
26,326✔
158
   * last block in chain to free blocks we already had.
159
   */
160

161
  union block_hdr *old_free_list = block_freelist;
162

26,326✔
163
  if (blok == NULL) {
164
    /* Don't free an empty pool. */
26,326✔
165
    return;
166
  }
167

168
  block_freelist = blok;
169

15,344✔
170
  /* Adjust first_avail pointers */
171

172
  while (blok->h.next) {
173
    chk_on_blk_list(blok, old_free_list, pool_tag);
16,452✔
174
    blok->h.first_avail = (char *) (blok + 1);
1,108✔
175
    blok = blok->h.next;
1,108✔
176
  }
1,108✔
177

178
  chk_on_blk_list(blok, old_free_list, pool_tag);
179
  blok->h.first_avail = (char *) (blok + 1);
15,344✔
180
  blok->h.next = old_free_list;
15,344✔
181
}
15,344✔
182

183
/* Get a new block, from the free list if possible, otherwise malloc a new
184
 * one.  minsz is the requested size of the block to be allocated.
185
 * If exact is TRUE, then minsz is the exact size of the allocated block;
186
 * otherwise, the allocated size will be rounded up from minsz to the nearest
187
 * multiple of BLOCK_MINFREE.
188
 *
189
 * Important: BLOCK ALARMS BEFORE CALLING
190
 */
191

192
static union block_hdr *new_block(int minsz, int exact) {
193
  union block_hdr **lastptr = &block_freelist;
17,222✔
194
  union block_hdr *blok = block_freelist;
17,222✔
195

17,222✔
196
  if (!exact) {
197
    minsz = 1 + ((minsz - 1) / BLOCK_MINFREE);
17,222✔
198
    minsz *= BLOCK_MINFREE;
16,321✔
199
  }
16,321✔
200

201
  /* Check if we have anything of the requested size on our free list first...
202
   */
203
  while (blok) {
204
    if (minsz <= ((char *) blok->h.endp - (char *) blok->h.first_avail)) {
17,345✔
205
      *lastptr = blok->h.next;
5,731✔
206
      blok->h.next = NULL;
5,608✔
207

5,608✔
208
      stat_freehit++;
209
      return blok;
5,608✔
210
    }
5,608✔
211

212
    lastptr = &blok->h.next;
213
    blok = blok->h.next;
123✔
214
  }
123✔
215

216
  /* Nope...damn.  Have to malloc() a new one. */
217
  stat_malloc++;
218
  return malloc_block(minsz);
11,614✔
219
}
11,614✔
220

221
struct cleanup;
222

223
static void run_cleanups(struct cleanup *c);
224

225
/* Pool internal and management */
226

227
struct pool_rec {
228
  union block_hdr *first;
229
  union block_hdr *last;
230
  struct cleanup *cleanups;
231
  struct pool_rec *sub_pools;
232
  struct pool_rec *sub_next;
233
  struct pool_rec *sub_prev;
234
  struct pool_rec *parent;
235
  char *free_first_avail;
236
  const char *tag;
237
};
238

239
pool *permanent_pool = NULL;
240
pool *global_config_pool = NULL;
241

242
/* Each pool structure is allocated in the start of it's own first block,
243
 * so there is a need to know how many bytes that is (once properly
244
 * aligned).
245
 */
246

247
#define POOL_HDR_CLICKS (1 + ((sizeof(struct pool_rec) - 1) / CLICK_SZ))
248
#define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
249

250
static unsigned long blocks_in_block_list(union block_hdr *blok) {
251
  unsigned long count = 0;
252

253
  while (blok) {
254
    count++;
171✔
255
    blok = blok->h.next;
99✔
256
  }
99✔
257

258
  return count;
259
}
63✔
260

261
static unsigned long bytes_in_block_list(union block_hdr *blok) {
262
  unsigned long size = 0;
63✔
263

63✔
264
  while (blok) {
265
    size += ((char *) blok->h.endp - (char *) (blok + 1));
171✔
266
    blok = blok->h.next;
99✔
267
  }
99✔
268

269
  return size;
270
}
271

272
static unsigned int subpools_in_pool(pool *p) {
273
  unsigned int count = 0;
139✔
274
  pool *iter;
139✔
275

139✔
276
  if (p->sub_pools == NULL) {
277
    return 0;
139✔
278
  }
279

280
  for (iter = p->sub_pools; iter; iter = iter->sub_next) {
281
    /* Count one for the current subpool (iter). */
135✔
282
    count += (subpools_in_pool(iter) + 1);
283
  }
76✔
284

285
  return count;
286
}
287

288
/* Visit all pools, starting with the top-level permanent pool, walking the
289
 * hierarchy.
290
 */
291
static unsigned long visit_pools(pool *p, unsigned long level,
292
    void (*visit)(const pr_pool_info_t *, void *), void *user_data) {
46✔
293
  unsigned long total_bytes = 0;
294

46✔
295
  if (p == NULL) {
296
    return 0;
46✔
297
  }
298

299
  for (; p; p = p->sub_next) {
300
    unsigned long byte_count = 0, block_count = 0;
109✔
301
    unsigned int subpool_count = 0;
63✔
302
    pr_pool_info_t pinfo;
63✔
303

63✔
304
    byte_count = bytes_in_block_list(p->first);
305
    block_count = blocks_in_block_list(p->first);
211✔
306
    subpool_count = subpools_in_pool(p);
63✔
307

63✔
308
    total_bytes += byte_count;
309

63✔
310
    memset(&pinfo, 0, sizeof(pinfo));
311
    pinfo.have_pool_info = TRUE;
63✔
312
    pinfo.tag = p->tag;
63✔
313
    pinfo.ptr = p;
63✔
314
    pinfo.byte_count = byte_count;
63✔
315
    pinfo.block_count = block_count;
63✔
316
    pinfo.subpool_count = subpool_count;
63✔
317
    pinfo.level = level;
63✔
318

63✔
319
    visit(&pinfo, user_data);
320

63✔
321
    /* Recurse */
322
    if (p->sub_pools) {
323
      total_bytes += visit_pools(p->sub_pools, level + 1, visit, user_data);
63✔
324
    }
35✔
325
  }
326

327
  return total_bytes;
328
}
329

330
static void pool_printf(const char *fmt, ...) {
331
  char buf[PR_TUNABLE_BUFFER_SIZE];
64✔
332
  va_list msg;
64✔
333

64✔
334
  memset(buf, '\0', sizeof(buf));
335

64✔
336
  va_start(msg, fmt);
337
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
64✔
338
  va_end(msg);
64✔
339

64✔
340
  buf[sizeof(buf)-1] = '\0';
341
  pr_trace_msg(trace_channel, 5, "%s", buf);
64✔
342
}
64✔
343

64✔
344
static void pool_visitf(const pr_pool_info_t *pinfo, void *user_data) {
345
  void (*debugf)(const char *, ...) = user_data;
46✔
346

46✔
347
  if (pinfo->have_pool_info) {
348

46✔
349
    /* The emitted message is:
350
     *
351
     *  <pool-tag> [pool-ptr] (n B, m L, r P)
352
     *
353
     * where n is the number of bytes (B), m is the number of allocated blocks
354
     * in the pool list (L), and r is the number of sub-pools (P).
355
     */
356

357
    if (pinfo->level == 0) {
358
      debugf("%s [%p] (%lu B, %lu L, %u P)",
34✔
359
        pinfo->tag ? pinfo->tag : "<unnamed>", pinfo->ptr,
6✔
360
        pinfo->byte_count, pinfo->block_count, pinfo->subpool_count);
6✔
361

6✔
362
    } else {
363
      char indent_text[80] = "";
364

28✔
365
      if (pinfo->level > 1) {
366
        memset(indent_text, ' ', sizeof(indent_text)-1);
28✔
367

13✔
368
        if ((pinfo->level - 1) * 3 >= sizeof(indent_text)) {
369
          indent_text[sizeof(indent_text)-1] = 0;
13✔
370

371
        } else {
372
          indent_text[(pinfo->level - 1) * 3] = '\0';
373
        }
13✔
374
      }
375

376
      debugf("%s + %s [%p] (%lu B, %lu L, %u P)", indent_text,
377
        pinfo->tag ? pinfo->tag : "<unnamed>", pinfo->ptr,
28✔
378
        pinfo->byte_count, pinfo->block_count, pinfo->subpool_count);
28✔
379
    }
28✔
380
  }
381

382
  if (pinfo->have_freelist_info) {
383
    debugf("Free block list: %lu bytes", pinfo->freelist_byte_count);
46✔
384
  }
6✔
385

386
  if (pinfo->have_total_info) {
387
    debugf("Total %lu bytes allocated", pinfo->total_byte_count);
46✔
388
    debugf("%lu blocks allocated", pinfo->total_blocks_allocated);
6✔
389
    debugf("%lu blocks reused", pinfo->total_blocks_reused);
6✔
390
  }
6✔
391
}
392

46✔
393
void pr_pool_debug_memory(void (*debugf)(const char *, ...)) {
394
  if (debugf == NULL) {
6✔
395
    debugf = pool_printf;
6✔
396
  }
6✔
397

398
  debugf("Memory pool allocation:");
399
  pr_pool_debug_memory2(pool_visitf, debugf);
6✔
400
}
6✔
401

6✔
402
void pr_pool_debug_memory2(void (*visit)(const pr_pool_info_t *, void *),
403
    void *user_data) {
12✔
404
  unsigned long freelist_byte_count = 0, freelist_block_count = 0,
405
    total_byte_count = 0;
12✔
406
  pr_pool_info_t pinfo;
12✔
407

12✔
408
  if (visit == NULL) {
409
    return;
12✔
410
  }
1✔
411

412
  /* Per pool */
413
  total_byte_count = visit_pools(permanent_pool, 0, visit, user_data);
414

11✔
415
  /* Free list */
416
  if (block_freelist) {
417
    freelist_byte_count = bytes_in_block_list(block_freelist);
11✔
418
    freelist_block_count = blocks_in_block_list(block_freelist);
23✔
419
  }
11✔
420

421
  memset(&pinfo, 0, sizeof(pinfo));
422
  pinfo.have_freelist_info = TRUE;
11✔
423
  pinfo.freelist_byte_count = freelist_byte_count;
11✔
424
  pinfo.freelist_block_count = freelist_block_count;
11✔
425

11✔
426
  visit(&pinfo, user_data);
427

11✔
428
  /* Totals */
429
  memset(&pinfo, 0, sizeof(pinfo));
430
  pinfo.have_total_info = TRUE;
11✔
431
  pinfo.total_byte_count = total_byte_count;
11✔
432
  pinfo.total_blocks_allocated = stat_malloc;
11✔
433
  pinfo.total_blocks_reused = stat_freehit;
11✔
434

11✔
435
  visit(&pinfo, user_data);
436
}
11✔
437

438
int pr_pool_debug_set_flags(int flags) {
439
  if (flags < 0) {
2✔
440
    errno = EINVAL;
2✔
441
    return -1;
1✔
442
  }
1✔
443

444
  debug_flags = flags;
445
  return 0;
1✔
446
}
1✔
447

448
void pr_pool_tag(pool *p, const char *tag) {
449
  if (p == NULL ||
12,706✔
450
      tag == NULL) {
12,706✔
451
    return;
12,691✔
452
  }
453

454
  p->tag = tag;
455
}
12,704✔
456

457
const char *pr_pool_get_tag(pool *p) {
458
  if (p == NULL) {
3✔
459
    errno = EINVAL;
3✔
460
    return NULL;
1✔
461
  }
1✔
462

463
  return p->tag;
464
}
2✔
465

466
/* Release the entire free block list */
467
static void pool_release_free_block_list(void) {
468
  union block_hdr *blok = NULL, *next = NULL;
15✔
469

15✔
470
  pr_alarms_block();
471

15✔
472
  for (blok = block_freelist; blok; blok = next) {
473
    next = blok->h.next;
146✔
474
    free(blok);
131✔
475
  }
131✔
476
  block_freelist = NULL;
477

15✔
478
  pr_alarms_unblock();
479
}
15✔
480

15✔
481
struct pool_rec *make_sub_pool(struct pool_rec *p) {
482
  union block_hdr *blok;
12,778✔
483
  pool *new_pool;
12,778✔
484

12,778✔
485
  pr_alarms_block();
486

12,778✔
487
  blok = new_block(0, FALSE);
488

12,778✔
489
  new_pool = (pool *) blok->h.first_avail;
490
  blok->h.first_avail = POOL_HDR_BYTES + (char *) blok->h.first_avail;
12,778✔
491

12,778✔
492
  memset(new_pool, 0, sizeof(struct pool_rec));
493
  new_pool->free_first_avail = blok->h.first_avail;
12,778✔
494
  new_pool->first = new_pool->last = blok;
12,778✔
495

12,778✔
496
  if (p != NULL) {
497
    new_pool->parent = p;
12,778✔
498
    new_pool->sub_next = p->sub_pools;
11,779✔
499

11,779✔
500
    if (new_pool->sub_next != NULL) {
501
      new_pool->sub_next->sub_prev = new_pool;
11,779✔
502
    }
8,586✔
503

504
    p->sub_pools = new_pool;
505
  }
11,779✔
506

507
  pr_alarms_unblock();
508

12,778✔
509
  return new_pool;
510
}
12,778✔
511

512
struct pool_rec *pr_pool_create_sz(struct pool_rec *p, size_t sz) {
513
  union block_hdr *blok;
899✔
514
  pool *new_pool;
899✔
515

899✔
516
  pr_alarms_block();
517

899✔
518
  blok = new_block(sz + POOL_HDR_BYTES, TRUE);
519

899✔
520
  new_pool = (pool *) blok->h.first_avail;
521
  blok->h.first_avail = POOL_HDR_BYTES + (char *) blok->h.first_avail;
899✔
522

899✔
523
  memset(new_pool, 0, sizeof(struct pool_rec));
524
  new_pool->free_first_avail = blok->h.first_avail;
899✔
525
  new_pool->first = new_pool->last = blok;
899✔
526

899✔
527
  if (p != NULL) {
528
    new_pool->parent = p;
899✔
529
    new_pool->sub_next = p->sub_pools;
893✔
530

893✔
531
    if (new_pool->sub_next != NULL) {
532
      new_pool->sub_next->sub_prev = new_pool;
893✔
533
    }
63✔
534

535
    p->sub_pools = new_pool;
536
  }
893✔
537

538
  pr_alarms_unblock();
539

899✔
540
  return new_pool;
541
}
899✔
542

543
/* Initialize the pool system by creating the base permanent_pool. */
544

545
void init_pools(void) {
546
  if (permanent_pool == NULL) {
15✔
547
    permanent_pool = make_sub_pool(NULL);
15✔
548
  }
15✔
549

550
  pr_pool_tag(permanent_pool, "permanent_pool");
551
}
15✔
552

15✔
553
void free_pools(void) {
554
  destroy_pool(permanent_pool);
15✔
555
  permanent_pool = NULL;
15✔
556
  pool_release_free_block_list();
15✔
557
}
15✔
558

15✔
559
static void clear_pool(struct pool_rec *p) {
560

13,163✔
561
  /* Sanity check. */
562
  if (p == NULL) {
563
    return;
13,163✔
564
  }
565

566
  pr_alarms_block();
567

13,163✔
568
  /* Run through any cleanups. */
569
  run_cleanups(p->cleanups);
570
  p->cleanups = NULL;
13,163✔
571

13,163✔
572
  /* Destroy subpools. */
573
  while (p->sub_pools != NULL) {
574
    destroy_pool(p->sub_pools);
18,664✔
575
  }
5,501✔
576

577
  p->sub_pools = NULL;
578

13,163✔
579
  free_blocks(p->first->h.next, p->tag);
580
  p->first->h.next = NULL;
13,163✔
581

13,163✔
582
  p->last = p->first;
583
  p->first->h.first_avail = p->free_first_avail;
13,163✔
584

13,163✔
585
  p->tag = NULL;
586
  pr_alarms_unblock();
13,163✔
587
}
13,163✔
588

589
void destroy_pool(pool *p) {
590
  if (p == NULL) {
13,180✔
591
    return;
13,180✔
592
  }
593

594
  pr_alarms_block();
595

13,163✔
596
  if (p->parent != NULL) {
597
    if (p->parent->sub_pools == p) {
13,163✔
598
      p->parent->sub_pools = p->sub_next;
12,271✔
599
    }
12,041✔
600

601
    if (p->sub_prev != NULL) {
602
      p->sub_prev->sub_next = p->sub_next;
12,271✔
603
    }
230✔
604

605
    if (p->sub_next != NULL) {
606
      p->sub_next->sub_prev = p->sub_prev;
12,271✔
607
    }
8,406✔
608
  }
609

610
  clear_pool(p);
611
  free_blocks(p->first, p->tag);
13,163✔
612

13,163✔
613
  pr_alarms_unblock();
614

13,163✔
615
#if defined(PR_DEVEL_NO_POOL_FREELIST)
616
  /* If configured explicitly to do so, call free(3) on the freelist after
617
   * a pool is destroyed.  This can be useful for tracking down use-after-free
618
   * and other memory issues using libraries such as dmalloc.
619
   */
620
  pool_release_free_block_list();
621
#endif /* PR_DEVEL_NO_POOL_FREELIST */
622
}
623

624
/* Allocation interface...
625
 */
626

627
static void *alloc_pool(struct pool_rec *p, size_t reqsz, int exact) {
628
  /* Round up requested size to an even number of aligned units */
35,455✔
629
  size_t nclicks = 1 + ((reqsz - 1) / CLICK_SZ);
630
  size_t sz = nclicks * CLICK_SZ;
35,455✔
631
  union block_hdr *blok;
35,455✔
632
  char *first_avail, *new_first_avail;
35,455✔
633

35,455✔
634
  if (p == NULL) {
635
    errno = EINVAL;
35,455✔
636
    return NULL;
×
637
  }
×
638

639
  /* For performance, see if space is available in the most recently
640
   * allocated block.
641
   */
642

643
  blok = p->last;
644
  if (blok == NULL) {
35,455✔
645
    errno = EINVAL;
35,455✔
646
    return NULL;
×
647
  }
×
648

649
  first_avail = blok->h.first_avail;
650

35,455✔
651
  if (reqsz == 0) {
652
    /* Don't try to allocate memory of zero length.
35,455✔
653
     *
654
     * This should NOT happen normally; if it does, by returning NULL we
655
     * almost guarantee a null pointer dereference.
656
     */
657
    errno = EINVAL;
658
    return NULL;
5✔
659
  }
5✔
660

661
  new_first_avail = first_avail + sz;
662

35,450✔
663
  if (new_first_avail <= (char *) blok->h.endp) {
664
    blok->h.first_avail = new_first_avail;
35,450✔
665
    return (void *) first_avail;
31,905✔
666
  }
31,905✔
667

668
  /* Need a new one that's big enough */
669
  pr_alarms_block();
670

3,545✔
671
  blok = new_block(sz, exact);
672
  p->last->h.next = blok;
3,545✔
673
  p->last = blok;
3,545✔
674

3,545✔
675
  first_avail = blok->h.first_avail;
676
  blok->h.first_avail = sz + (char *) blok->h.first_avail;
3,545✔
677

3,545✔
678
  pr_alarms_unblock();
679
  return (void *) first_avail;
3,545✔
680
}
3,545✔
681

682
void *palloc(struct pool_rec *p, size_t sz) {
683
  return alloc_pool(p, sz, FALSE);
35,449✔
684
}
7,796✔
685

686
void *pallocsz(struct pool_rec *p, size_t sz) {
687
  return alloc_pool(p, sz, TRUE);
6✔
688
}
3✔
689

690
void *pcalloc(struct pool_rec *p, size_t sz) {
691
  void *res;
27,262✔
692

27,262✔
693
  if (p == NULL) {
694
    errno = EINVAL;
27,262✔
695
    return NULL;
×
696
  }
×
697

698
  res = palloc(p, sz);
699
  memset(res, '\0', sz);
27,262✔
700

27,262✔
701
  return res;
702
}
27,262✔
703

704
void *pcallocsz(struct pool_rec *p, size_t sz) {
705
  void *res;
3✔
706

3✔
707
  if (p == NULL) {
708
    errno = EINVAL;
3✔
709
    return NULL;
×
710
  }
×
711

712
  res = pallocsz(p, sz);
713
  memset(res, '\0', sz);
3✔
714

3✔
715
  return res;
716
}
3✔
717

718
/* Array functions */
719

720
array_header *make_array(pool *p, unsigned int nelts, size_t elt_size) {
721
  array_header *res;
392✔
722

392✔
723
  if (p == NULL ||
724
      elt_size == 0) {
392✔
725
    errno = EINVAL;
392✔
726
    return NULL;
3✔
727
  }
3✔
728

729
  res = palloc(p, sizeof(array_header));
730

389✔
731
  if (nelts < 1) {
732
    nelts = 1;
389✔
733
  }
734

735
  res->elts = pcalloc(p, nelts * elt_size);
736
  res->pool = p;
389✔
737
  res->elt_size = elt_size;
389✔
738
  res->nelts = 0;
389✔
739
  res->nalloc = nelts;
389✔
740

389✔
741
  return res;
742
}
389✔
743

744
void clear_array(array_header *arr) {
745
  if (arr == NULL) {
5✔
746
    return;
5✔
747
  }
748

749
  arr->elts = pcalloc(arr->pool, arr->nalloc * arr->elt_size);
750
  arr->nelts = 0;
4✔
751
}
4✔
752

753
void *push_array(array_header *arr) {
754
  if (arr == NULL) {
685✔
755
    errno = EINVAL;
685✔
756
    return NULL;
1✔
757
  }
1✔
758

759
  if (arr->nelts == arr->nalloc) {
760
    char *new_data = pcalloc(arr->pool, arr->nalloc * arr->elt_size * 2);
684✔
761

204✔
762
    memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
763
    arr->elts = new_data;
204✔
764
    arr->nalloc *= 2;
204✔
765
  }
204✔
766

767
  ++arr->nelts;
768
  return ((char *) arr->elts) + (arr->elt_size * (arr->nelts - 1));
684✔
769
}
684✔
770

771
int array_cat2(array_header *dst, const array_header *src) {
772
  size_t elt_size;
16✔
773

16✔
774
  if (dst == NULL ||
775
      src == NULL) {
16✔
776
    errno = EINVAL;
16✔
777
    return -1;
6✔
778
  }
6✔
779

780
  elt_size = dst->elt_size;
781

10✔
782
  if (dst->nelts + src->nelts > dst->nalloc) {
783
    size_t new_size;
10✔
784
    char *new_data;
6✔
785

6✔
786
    new_size = dst->nalloc * 2;
787
    if (new_size == 0) {
6✔
788
      ++new_size;
6✔
789
    }
790

791
    while ((dst->nelts + src->nelts) > new_size) {
792
      new_size *= 2;
14✔
793
    }
8✔
794

795
    new_data = pcalloc(dst->pool, elt_size * new_size);
796
    memcpy(new_data, dst->elts, dst->nalloc * elt_size);
6✔
797

6✔
798
    dst->elts = new_data;
799
    dst->nalloc = new_size;
6✔
800
  }
6✔
801

802
  memcpy(((char *) dst->elts) + (dst->nelts * elt_size), (char *) src->elts,
803
         elt_size * src->nelts);
10✔
804
  dst->nelts += src->nelts;
10✔
805

10✔
806
  return 0;
807
}
10✔
808

809
void array_cat(array_header *dst, const array_header *src) {
810
  (void) array_cat2(dst, src);
10✔
811
}
9✔
812

9✔
813
array_header *copy_array(pool *p, const array_header *arr) {
814
  array_header *res;
8✔
815

8✔
816
  if (p == NULL ||
817
      arr == NULL) {
8✔
818
    errno = EINVAL;
8✔
819
    return NULL;
4✔
820
  }
4✔
821

822
  res = make_array(p, arr->nalloc, arr->elt_size);
823

4✔
824
  if (arr->nelts > 0) {
825
    memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts);
4✔
826
  }
4✔
827

828
  res->nelts = arr->nelts;
829
  return res;
4✔
830
}
4✔
831

832
/* copy an array that is assumed to consist solely of strings */
833
array_header *copy_array_str(pool *p, const array_header *arr) {
834
  register unsigned int i;
4✔
835
  array_header *res;
4✔
836

4✔
837
  if (p == NULL ||
838
      arr == NULL) {
4✔
839
    errno = EINVAL;
4✔
840
    return NULL;
3✔
841
  }
3✔
842

843
  res = copy_array(p, arr);
844

1✔
845
  for (i = 0; i < arr->nelts; i++) {
846
    ((char **) res->elts)[i] = pstrdup(p, ((char **) res->elts)[i]);
4✔
847
  }
2✔
848

849
  return res;
850
}
851

852
array_header *copy_array_hdr(pool *p, const array_header *arr) {
853
  array_header *res;
5✔
854

5✔
855
  if (p == NULL ||
856
      arr == NULL) {
5✔
857
    errno = EINVAL;
5✔
858
    return NULL;
3✔
859
  }
3✔
860

861
  res = palloc(p, sizeof(array_header));
862

2✔
863
  res->elts = arr->elts;
864
  res->pool = p;
2✔
865
  res->elt_size = arr->elt_size;
2✔
866
  res->nelts = arr->nelts;
2✔
867
  res->nalloc = arr->nelts;                /* Force overflow on push */
2✔
868

2✔
869
  return res;
870
}
2✔
871

872
array_header *append_arrays(pool *p, const array_header *first,
873
    const array_header *second) {
8✔
874
  array_header *res;
875

8✔
876
  if (p == NULL ||
877
      first == NULL ||
8✔
878
      second == NULL) {
8✔
879
    errno = EINVAL;
880
    return NULL;
7✔
881
  }
7✔
882

883
  res = copy_array_hdr(p, first);
884

1✔
885
  array_cat(res, second);
886
  return res;
1✔
887
}
1✔
888

889
/* Generic cleanups */
890

891
typedef struct cleanup {
892
  void *user_data;
893
  void (*cleanup_cb)(void *);
894
  struct cleanup *next;
895

896
} cleanup_t;
897

898
void register_cleanup2(pool *p, void *user_data, void (*cleanup_cb)(void*)) {
899
  cleanup_t *c;
925✔
900

925✔
901
  if (p == NULL) {
902
    return;
925✔
903
  }
904

905
  c = pcalloc(p, sizeof(cleanup_t));
906
  c->user_data = user_data;
923✔
907
  c->cleanup_cb = cleanup_cb;
923✔
908

923✔
909
  /* Add this cleanup to the given pool's list of cleanups. */
910
  c->next = p->cleanups;
911
  p->cleanups = c;
923✔
912
}
923✔
913

914
void register_cleanup(pool *p, void *user_data, void (*plain_cleanup_cb)(void*),
915
    void (*child_cleanup_cb)(void *)) {
5✔
916
  (void) child_cleanup_cb;
917
  register_cleanup2(p, user_data, plain_cleanup_cb);
5✔
918
}
5✔
919

5✔
920
void unregister_cleanup(pool *p, void *user_data, void (*cleanup_cb)(void *)) {
921
  cleanup_t *c, **lastp;
3✔
922

3✔
923
  if (p == NULL) {
924
    return;
3✔
925
  }
926

927
  c = p->cleanups;
928
  lastp = &p->cleanups;
2✔
929

2✔
930
  while (c != NULL) {
931
    if (c->user_data == user_data &&
2✔
932
        (c->cleanup_cb == cleanup_cb || cleanup_cb == NULL)) {
2✔
933

2✔
934
      /* Remove the given cleanup by pointing the previous next pointer to
935
       * the matching cleanup's next pointer.
936
       */
937
      *lastp = c->next;
938
      break;
2✔
939
    }
2✔
940

941
    lastp = &c->next;
942
    c = c->next;
×
943
  }
×
944
}
945

946
static void run_cleanups(cleanup_t *c) {
947
  while (c != NULL) {
13,163✔
948
    if (c->cleanup_cb) {
14,020✔
949
      (*c->cleanup_cb)(c->user_data);
857✔
950
    }
855✔
951

952
    c = c->next;
953
  }
857✔
954
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc