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

proftpd / proftpd / 26693818498

30 May 2026 08:22PM UTC coverage: 93.024% (+0.4%) from 92.637%
26693818498

push

github

51329 of 55178 relevant lines covered (93.02%)

222.12 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
#if defined(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
#if defined(PR_USE_DEVEL)
×
91
  if (debug_flags & PR_POOL_DEBUG_FL_OOM_DUMP_POOLS) {
92
    pr_pool_debug_memory(oom_printf);
×
93
  }
×
94
#endif /* PR_USE_DEVEL */
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
#if defined(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(size_t 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 == FALSE) {
197
    if (minsz == 0) {
17,222✔
198
      minsz = 1;
16,321✔
199
    }
16,321✔
200

201
    minsz = 1 + ((minsz - 1) / BLOCK_MINFREE);
202
    minsz *= BLOCK_MINFREE;
203
  }
204

17,345✔
205
  /* Check if we have anything of the requested size on our free list first...
5,731✔
206
   */
5,608✔
207
  while (blok) {
5,608✔
208
    size_t availsz;
209

5,608✔
210
    availsz = ((char *) blok->h.endp - (char *) blok->h.first_avail);
5,608✔
211
    if (minsz <= availsz) {
212
      *lastptr = blok->h.next;
213
      blok->h.next = NULL;
123✔
214

123✔
215
      stat_freehit++;
216
      return blok;
217
    }
218

11,614✔
219
    lastptr = &blok->h.next;
11,614✔
220
    blok = blok->h.next;
221
  }
222

223
  /* Nope...damn.  Have to malloc() a new one. */
224
  stat_malloc++;
225
  return malloc_block(minsz);
226
}
227

228
struct cleanup;
229

230
static void run_cleanups(struct cleanup *c);
231

232
/* Pool internal and management */
233

234
struct pool_rec {
235
  union block_hdr *first;
236
  union block_hdr *last;
237
  struct cleanup *cleanups;
238
  struct pool_rec *sub_pools;
239
  struct pool_rec *sub_next;
240
  struct pool_rec *sub_prev;
241
  struct pool_rec *parent;
242
  char *free_first_avail;
243
  const char *tag;
244
};
245

246
pool *permanent_pool = NULL;
247
pool *global_config_pool = NULL;
248

249
/* Each pool structure is allocated in the start of it's own first block,
250
 * so there is a need to know how many bytes that is (once properly
251
 * aligned).
252
 */
253

254
#define POOL_HDR_CLICKS (1 + ((sizeof(struct pool_rec) - 1) / CLICK_SZ))
171✔
255
#define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
99✔
256

99✔
257
static unsigned long blocks_in_block_list(union block_hdr *blok) {
258
  unsigned long count = 0;
259

63✔
260
  while (blok) {
261
    count++;
262
    blok = blok->h.next;
63✔
263
  }
63✔
264

265
  return count;
171✔
266
}
99✔
267

99✔
268
static unsigned long bytes_in_block_list(union block_hdr *blok) {
269
  unsigned long size = 0;
270

271
  while (blok) {
272
    size += ((char *) blok->h.endp - (char *) (blok + 1));
273
    blok = blok->h.next;
139✔
274
  }
139✔
275

139✔
276
  return size;
277
}
139✔
278

279
static unsigned int subpools_in_pool(pool *p) {
280
  unsigned int count = 0;
281
  pool *iter;
135✔
282

283
  if (p->sub_pools == NULL) {
76✔
284
    return 0;
285
  }
286

287
  for (iter = p->sub_pools; iter; iter = iter->sub_next) {
288
    /* Count one for the current subpool (iter). */
289
    count += (subpools_in_pool(iter) + 1);
290
  }
291

292
  return count;
46✔
293
}
294

46✔
295
/* Visit all pools, starting with the top-level permanent pool, walking the
296
 * hierarchy.
46✔
297
 */
298
static unsigned long visit_pools(pool *p, unsigned long level,
299
    void (*visit)(const pr_pool_info_t *, void *), void *user_data) {
300
  unsigned long total_bytes = 0;
109✔
301

63✔
302
  if (p == NULL) {
63✔
303
    return 0;
63✔
304
  }
305

211✔
306
  for (; p; p = p->sub_next) {
63✔
307
    unsigned long byte_count = 0, block_count = 0;
63✔
308
    unsigned int subpool_count = 0;
309
    pr_pool_info_t pinfo;
63✔
310

311
    byte_count = bytes_in_block_list(p->first);
63✔
312
    block_count = blocks_in_block_list(p->first);
63✔
313
    subpool_count = subpools_in_pool(p);
63✔
314

63✔
315
    total_bytes += byte_count;
63✔
316

63✔
317
    memset(&pinfo, 0, sizeof(pinfo));
63✔
318
    pinfo.have_pool_info = TRUE;
63✔
319
    pinfo.tag = p->tag;
320
    pinfo.ptr = p;
63✔
321
    pinfo.byte_count = byte_count;
322
    pinfo.block_count = block_count;
323
    pinfo.subpool_count = subpool_count;
63✔
324
    pinfo.level = level;
35✔
325

326
    visit(&pinfo, user_data);
327

328
    /* Recurse */
329
    if (p->sub_pools) {
330
      total_bytes += visit_pools(p->sub_pools, level + 1, visit, user_data);
331
    }
64✔
332
  }
64✔
333

64✔
334
  return total_bytes;
335
}
64✔
336

337
static void pool_printf(const char *fmt, ...) {
64✔
338
  char buf[PR_TUNABLE_BUFFER_SIZE];
64✔
339
  va_list msg;
64✔
340

341
  memset(buf, '\0', sizeof(buf));
64✔
342

64✔
343
  va_start(msg, fmt);
64✔
344
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
345
  va_end(msg);
46✔
346

46✔
347
  buf[sizeof(buf)-1] = '\0';
348
  pr_trace_msg(trace_channel, 5, "%s", buf);
46✔
349
}
350

351
static void pool_visitf(const pr_pool_info_t *pinfo, void *user_data) {
352
  void (*debugf)(const char *, ...) = user_data;
353

354
  if (pinfo->have_pool_info) {
355

356
    /* The emitted message is:
357
     *
358
     *  <pool-tag> [pool-ptr] (n B, m L, r P)
34✔
359
     *
6✔
360
     * where n is the number of bytes (B), m is the number of allocated blocks
6✔
361
     * in the pool list (L), and r is the number of sub-pools (P).
6✔
362
     */
363

364
    if (pinfo->level == 0) {
28✔
365
      debugf("%s [%p] (%lu B, %lu L, %u P)",
366
        pinfo->tag ? pinfo->tag : "<unnamed>", pinfo->ptr,
28✔
367
        pinfo->byte_count, pinfo->block_count, pinfo->subpool_count);
13✔
368

369
    } else {
13✔
370
      char indent_text[80] = "";
371

372
      if (pinfo->level > 1) {
373
        memset(indent_text, ' ', sizeof(indent_text)-1);
13✔
374

375
        if ((pinfo->level - 1) * 3 >= sizeof(indent_text)) {
376
          indent_text[sizeof(indent_text)-1] = 0;
377

28✔
378
        } else {
28✔
379
          indent_text[(pinfo->level - 1) * 3] = '\0';
28✔
380
        }
381
      }
382

383
      debugf("%s + %s [%p] (%lu B, %lu L, %u P)", indent_text,
46✔
384
        pinfo->tag ? pinfo->tag : "<unnamed>", pinfo->ptr,
6✔
385
        pinfo->byte_count, pinfo->block_count, pinfo->subpool_count);
386
    }
387
  }
46✔
388

6✔
389
  if (pinfo->have_freelist_info) {
6✔
390
    debugf("Free block list: %lu bytes", pinfo->freelist_byte_count);
6✔
391
  }
392

46✔
393
  if (pinfo->have_total_info) {
394
    debugf("Total %lu bytes allocated", pinfo->total_byte_count);
6✔
395
    debugf("%lu blocks allocated", pinfo->total_blocks_allocated);
6✔
396
    debugf("%lu blocks reused", pinfo->total_blocks_reused);
6✔
397
  }
398
}
399

6✔
400
void pr_pool_debug_memory(void (*debugf)(const char *, ...)) {
6✔
401
  if (debugf == NULL) {
6✔
402
    debugf = pool_printf;
403
  }
12✔
404

405
  debugf("Memory pool allocation:");
12✔
406
  pr_pool_debug_memory2(pool_visitf, debugf);
12✔
407
}
12✔
408

409
void pr_pool_debug_memory2(void (*visit)(const pr_pool_info_t *, void *),
12✔
410
    void *user_data) {
1✔
411
  unsigned long freelist_byte_count = 0, freelist_block_count = 0,
412
    total_byte_count = 0;
413
  pr_pool_info_t pinfo;
414

11✔
415
  if (visit == NULL) {
416
    return;
417
  }
11✔
418

23✔
419
  /* Per pool */
11✔
420
  total_byte_count = visit_pools(permanent_pool, 0, visit, user_data);
421

422
  /* Free list */
11✔
423
  if (block_freelist) {
11✔
424
    freelist_byte_count = bytes_in_block_list(block_freelist);
11✔
425
    freelist_block_count = blocks_in_block_list(block_freelist);
11✔
426
  }
427

11✔
428
  memset(&pinfo, 0, sizeof(pinfo));
429
  pinfo.have_freelist_info = TRUE;
430
  pinfo.freelist_byte_count = freelist_byte_count;
11✔
431
  pinfo.freelist_block_count = freelist_block_count;
11✔
432

11✔
433
  visit(&pinfo, user_data);
11✔
434

11✔
435
  /* Totals */
436
  memset(&pinfo, 0, sizeof(pinfo));
11✔
437
  pinfo.have_total_info = TRUE;
438
  pinfo.total_byte_count = total_byte_count;
439
  pinfo.total_blocks_allocated = stat_malloc;
2✔
440
  pinfo.total_blocks_reused = stat_freehit;
2✔
441

1✔
442
  visit(&pinfo, user_data);
1✔
443
}
444

445
int pr_pool_debug_set_flags(int flags) {
1✔
446
  if (flags < 0) {
1✔
447
    errno = EINVAL;
448
    return -1;
449
  }
12,706✔
450

12,706✔
451
  debug_flags = flags;
12,691✔
452
  return 0;
453
}
454

455
void pr_pool_tag(pool *p, const char *tag) {
12,704✔
456
  if (p == NULL ||
457
      tag == NULL) {
458
    return;
3✔
459
  }
3✔
460

1✔
461
  p->tag = tag;
1✔
462
}
463

464
const char *pr_pool_get_tag(pool *p) {
2✔
465
  if (p == NULL) {
466
    errno = EINVAL;
467
    return NULL;
468
  }
15✔
469

15✔
470
  return p->tag;
471
}
15✔
472

473
/* Release the entire free block list */
146✔
474
static void pool_release_free_block_list(void) {
131✔
475
  union block_hdr *blok = NULL, *next = NULL;
131✔
476

477
  pr_alarms_block();
15✔
478

479
  for (blok = block_freelist; blok; blok = next) {
15✔
480
    next = blok->h.next;
15✔
481
    free(blok);
482
  }
12,778✔
483
  block_freelist = NULL;
12,778✔
484

12,778✔
485
  pr_alarms_unblock();
486
}
12,778✔
487

488
struct pool_rec *make_sub_pool(struct pool_rec *p) {
12,778✔
489
  union block_hdr *blok;
490
  pool *new_pool;
12,778✔
491

12,778✔
492
  pr_alarms_block();
493

12,778✔
494
  blok = new_block(0, FALSE);
12,778✔
495

12,778✔
496
  new_pool = (pool *) blok->h.first_avail;
497
  blok->h.first_avail = POOL_HDR_BYTES + (char *) blok->h.first_avail;
12,778✔
498

11,779✔
499
  memset(new_pool, 0, sizeof(struct pool_rec));
11,779✔
500
  new_pool->free_first_avail = blok->h.first_avail;
501
  new_pool->first = new_pool->last = blok;
11,779✔
502

8,586✔
503
  if (p != NULL) {
504
    new_pool->parent = p;
505
    new_pool->sub_next = p->sub_pools;
11,779✔
506

507
    if (new_pool->sub_next != NULL) {
508
      new_pool->sub_next->sub_prev = new_pool;
12,778✔
509
    }
510

12,778✔
511
    p->sub_pools = new_pool;
512
  }
513

899✔
514
  pr_alarms_unblock();
899✔
515

899✔
516
  return new_pool;
517
}
899✔
518

519
struct pool_rec *pr_pool_create_sz(struct pool_rec *p, size_t sz) {
899✔
520
  union block_hdr *blok;
521
  pool *new_pool;
899✔
522

899✔
523
  pr_alarms_block();
524

899✔
525
  blok = new_block(sz + POOL_HDR_BYTES, TRUE);
899✔
526

899✔
527
  new_pool = (pool *) blok->h.first_avail;
528
  blok->h.first_avail = POOL_HDR_BYTES + (char *) blok->h.first_avail;
899✔
529

893✔
530
  memset(new_pool, 0, sizeof(struct pool_rec));
893✔
531
  new_pool->free_first_avail = blok->h.first_avail;
532
  new_pool->first = new_pool->last = blok;
893✔
533

63✔
534
  if (p != NULL) {
535
    new_pool->parent = p;
536
    new_pool->sub_next = p->sub_pools;
893✔
537

538
    if (new_pool->sub_next != NULL) {
539
      new_pool->sub_next->sub_prev = new_pool;
899✔
540
    }
541

899✔
542
    p->sub_pools = new_pool;
543
  }
544

545
  pr_alarms_unblock();
546

15✔
547
  return new_pool;
15✔
548
}
15✔
549

550
/* Initialize the pool system by creating the base permanent_pool. */
551

15✔
552
void init_pools(void) {
15✔
553
  if (permanent_pool == NULL) {
554
    permanent_pool = make_sub_pool(NULL);
15✔
555
  }
15✔
556

15✔
557
  pr_pool_tag(permanent_pool, "permanent_pool");
15✔
558
}
15✔
559

560
void free_pools(void) {
13,163✔
561
  destroy_pool(permanent_pool);
562
  permanent_pool = NULL;
563
  session.pool = NULL;
13,163✔
564
  session.notes = NULL;
565
  pool_release_free_block_list();
566
}
567

13,163✔
568
static void clear_pool(struct pool_rec *p) {
569

570
  /* Sanity check. */
13,163✔
571
  if (p == NULL) {
13,163✔
572
    return;
573
  }
574

18,664✔
575
  pr_alarms_block();
5,501✔
576

577
  /* Run through any cleanups. */
578
  run_cleanups(p->cleanups);
13,163✔
579
  p->cleanups = NULL;
580

13,163✔
581
  /* Destroy subpools. */
13,163✔
582
  while (p->sub_pools != NULL) {
583
    destroy_pool(p->sub_pools);
13,163✔
584
  }
13,163✔
585

586
  p->sub_pools = NULL;
13,163✔
587

13,163✔
588
  free_blocks(p->first->h.next, p->tag);
589
  p->first->h.next = NULL;
590

13,180✔
591
  p->last = p->first;
13,180✔
592
  p->first->h.first_avail = p->free_first_avail;
593

594
  p->tag = NULL;
595
  pr_alarms_unblock();
13,163✔
596
}
597

13,163✔
598
void destroy_pool(pool *p) {
12,271✔
599
  if (p == NULL) {
12,041✔
600
    return;
601
  }
602

12,271✔
603
  pr_alarms_block();
230✔
604

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

610
    if (p->sub_prev != NULL) {
611
      p->sub_prev->sub_next = p->sub_next;
13,163✔
612
    }
13,163✔
613

614
    if (p->sub_next != NULL) {
13,163✔
615
      p->sub_next->sub_prev = p->sub_prev;
616
    }
617
  }
618

619
  clear_pool(p);
620
  free_blocks(p->first, p->tag);
621

622
  pr_alarms_unblock();
623

624
#if defined(PR_DEVEL_NO_POOL_FREELIST)
625
  /* If configured explicitly to do so, call free(3) on the freelist after
626
   * a pool is destroyed.  This can be useful for tracking down use-after-free
627
   * and other memory issues using libraries such as dmalloc.
628
   */
35,455✔
629
  pool_release_free_block_list();
630
#endif /* PR_DEVEL_NO_POOL_FREELIST */
35,455✔
631
}
35,455✔
632

35,455✔
633
/* Allocation interface...
35,455✔
634
 */
635

35,455✔
636
static void *alloc_pool(struct pool_rec *p, size_t reqsz, int exact) {
×
637
  /* Round up requested size to an even number of aligned units */
×
638
  size_t nclicks = 1 + ((reqsz - 1) / CLICK_SZ);
639
  size_t sz = nclicks * CLICK_SZ;
640
  union block_hdr *blok;
641
  char *first_avail, *new_first_avail;
642

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

×
648
  /* For performance, see if space is available in the most recently
649
   * allocated block.
650
   */
35,455✔
651

652
  blok = p->last;
35,455✔
653
  if (blok == NULL) {
654
    errno = EINVAL;
655
    return NULL;
656
  }
657

658
  first_avail = blok->h.first_avail;
5✔
659

5✔
660
  if (reqsz == 0) {
661
    /* Don't try to allocate memory of zero length.
662
     *
35,450✔
663
     * This should NOT happen normally; if it does, by returning NULL we
664
     * almost guarantee a null pointer dereference.
35,450✔
665
     */
31,905✔
666
    errno = EINVAL;
31,905✔
667
    return NULL;
668
  }
669

670
  new_first_avail = first_avail + sz;
3,545✔
671

672
  if (new_first_avail <= (char *) blok->h.endp) {
3,545✔
673
    blok->h.first_avail = new_first_avail;
3,545✔
674
    return (void *) first_avail;
3,545✔
675
  }
676

3,545✔
677
  /* Need a new one that's big enough */
3,545✔
678
  pr_alarms_block();
679

3,545✔
680
  blok = new_block(sz, exact);
3,545✔
681
  p->last->h.next = blok;
682
  p->last = blok;
683

35,449✔
684
  first_avail = blok->h.first_avail;
7,796✔
685
  blok->h.first_avail = sz + (char *) blok->h.first_avail;
686

687
  pr_alarms_unblock();
6✔
688
  return (void *) first_avail;
3✔
689
}
690

691
void *palloc(struct pool_rec *p, size_t sz) {
27,262✔
692
  return alloc_pool(p, sz, FALSE);
27,262✔
693
}
694

27,262✔
695
void *pallocsz(struct pool_rec *p, size_t sz) {
×
696
  return alloc_pool(p, sz, TRUE);
×
697
}
698

699
void *pcalloc(struct pool_rec *p, size_t sz) {
27,262✔
700
  void *res;
27,262✔
701

702
  if (p == NULL ||
27,262✔
703
      sz == 0) {
704
    errno = EINVAL;
705
    return NULL;
3✔
706
  }
3✔
707

708
  res = palloc(p, sz);
3✔
709
  memset(res, '\0', sz);
×
710

×
711
  return res;
712
}
713

3✔
714
void *pcallocsz(struct pool_rec *p, size_t sz) {
3✔
715
  void *res;
716

3✔
717
  if (p == NULL ||
718
      sz == 0) {
719
    errno = EINVAL;
720
    return NULL;
721
  }
392✔
722

392✔
723
  res = pallocsz(p, sz);
724
  memset(res, '\0', sz);
392✔
725

392✔
726
  return res;
3✔
727
}
3✔
728

729
/* Array functions */
730

389✔
731
array_header *make_array(pool *p, unsigned int nelts, size_t elt_size) {
732
  array_header *res;
389✔
733

734
  if (p == NULL ||
735
      elt_size == 0) {
736
    errno = EINVAL;
389✔
737
    return NULL;
389✔
738
  }
389✔
739

389✔
740
  res = palloc(p, sizeof(array_header));
389✔
741

742
  if (nelts < 1) {
389✔
743
    nelts = 1;
744
  }
745

5✔
746
  res->elts = pcalloc(p, nelts * elt_size);
5✔
747
  res->pool = p;
748
  res->elt_size = elt_size;
749
  res->nelts = 0;
750
  res->nalloc = nelts;
4✔
751

4✔
752
  return res;
753
}
754

685✔
755
void clear_array(array_header *arr) {
685✔
756
  if (arr == NULL) {
1✔
757
    return;
1✔
758
  }
759

760
  arr->elts = pcalloc(arr->pool, arr->nalloc * arr->elt_size);
684✔
761
  arr->nelts = 0;
204✔
762
}
763

204✔
764
void *push_array(array_header *arr) {
204✔
765
  if (arr == NULL) {
204✔
766
    errno = EINVAL;
767
    return NULL;
768
  }
684✔
769

684✔
770
  if (arr->nelts == arr->nalloc) {
771
    char *new_data = pcalloc(arr->pool, arr->nalloc * arr->elt_size * 2);
772

16✔
773
    memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
16✔
774
    arr->elts = new_data;
775
    arr->nalloc *= 2;
16✔
776
  }
16✔
777

6✔
778
  ++arr->nelts;
6✔
779
  return ((char *) arr->elts) + (arr->elt_size * (arr->nelts - 1));
780
}
781

10✔
782
int array_cat2(array_header *dst, const array_header *src) {
783
  size_t elt_size;
10✔
784

6✔
785
  if (dst == NULL ||
6✔
786
      src == NULL) {
787
    errno = EINVAL;
6✔
788
    return -1;
6✔
789
  }
790

791
  elt_size = dst->elt_size;
792

14✔
793
  if (dst->nelts + src->nelts > dst->nalloc) {
8✔
794
    size_t new_size;
795
    char *new_data;
796

6✔
797
    new_size = dst->nalloc * 2;
6✔
798
    if (new_size == 0) {
799
      ++new_size;
6✔
800
    }
6✔
801

802
    while ((dst->nelts + src->nelts) > new_size) {
803
      new_size *= 2;
10✔
804
    }
10✔
805

10✔
806
    new_data = pcalloc(dst->pool, elt_size * new_size);
807
    memcpy(new_data, dst->elts, dst->nalloc * elt_size);
10✔
808

809
    dst->elts = new_data;
810
    dst->nalloc = new_size;
10✔
811
  }
9✔
812

9✔
813
  memcpy(((char *) dst->elts) + (dst->nelts * elt_size), (char *) src->elts,
814
         elt_size * src->nelts);
8✔
815
  dst->nelts += src->nelts;
8✔
816

817
  return 0;
8✔
818
}
8✔
819

4✔
820
void array_cat(array_header *dst, const array_header *src) {
4✔
821
  (void) array_cat2(dst, src);
822
}
823

4✔
824
array_header *copy_array(pool *p, const array_header *arr) {
825
  array_header *res;
4✔
826

4✔
827
  if (p == NULL ||
828
      arr == NULL) {
829
    errno = EINVAL;
4✔
830
    return NULL;
4✔
831
  }
832

833
  res = make_array(p, arr->nalloc, arr->elt_size);
834

4✔
835
  if (arr->nelts > 0) {
4✔
836
    memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts);
4✔
837
  }
838

4✔
839
  res->nelts = arr->nelts;
4✔
840
  return res;
3✔
841
}
3✔
842

843
/* copy an array that is assumed to consist solely of strings */
844
array_header *copy_array_str(pool *p, const array_header *arr) {
1✔
845
  register unsigned int i;
846
  array_header *res;
4✔
847

2✔
848
  if (p == NULL ||
849
      arr == NULL) {
850
    errno = EINVAL;
851
    return NULL;
852
  }
853

5✔
854
  res = copy_array(p, arr);
5✔
855

856
  for (i = 0; i < arr->nelts; i++) {
5✔
857
    ((char **) res->elts)[i] = pstrdup(p, ((char **) res->elts)[i]);
5✔
858
  }
3✔
859

3✔
860
  return res;
861
}
862

2✔
863
array_header *copy_array_hdr(pool *p, const array_header *arr) {
864
  array_header *res;
2✔
865

2✔
866
  if (p == NULL ||
2✔
867
      arr == NULL) {
2✔
868
    errno = EINVAL;
2✔
869
    return NULL;
870
  }
2✔
871

872
  res = palloc(p, sizeof(array_header));
873

8✔
874
  res->elts = arr->elts;
875
  res->pool = p;
8✔
876
  res->elt_size = arr->elt_size;
877
  res->nelts = arr->nelts;
8✔
878
  res->nalloc = arr->nelts;                /* Force overflow on push */
8✔
879

880
  return res;
7✔
881
}
7✔
882

883
array_header *append_arrays(pool *p, const array_header *first,
884
    const array_header *second) {
1✔
885
  array_header *res;
886

1✔
887
  if (p == NULL ||
1✔
888
      first == NULL ||
889
      second == NULL) {
890
    errno = EINVAL;
891
    return NULL;
892
  }
893

894
  res = copy_array_hdr(p, first);
895

896
  array_cat(res, second);
897
  return res;
898
}
899

925✔
900
/* Generic cleanups */
925✔
901

902
typedef struct cleanup {
925✔
903
  void *user_data;
904
  void (*cleanup_cb)(void *);
905
  struct cleanup *next;
906

923✔
907
} cleanup_t;
923✔
908

923✔
909
void register_cleanup2(pool *p, void *user_data, void (*cleanup_cb)(void*)) {
910
  cleanup_t *c;
911

923✔
912
  if (p == NULL) {
923✔
913
    return;
914
  }
915

5✔
916
  c = pcalloc(p, sizeof(cleanup_t));
917
  c->user_data = user_data;
5✔
918
  c->cleanup_cb = cleanup_cb;
5✔
919

5✔
920
  /* Add this cleanup to the given pool's list of cleanups. */
921
  c->next = p->cleanups;
3✔
922
  p->cleanups = c;
3✔
923
}
924

3✔
925
void register_cleanup(pool *p, void *user_data, void (*plain_cleanup_cb)(void*),
926
    void (*child_cleanup_cb)(void *)) {
927
  (void) child_cleanup_cb;
928
  register_cleanup2(p, user_data, plain_cleanup_cb);
2✔
929
}
2✔
930

931
void unregister_cleanup(pool *p, void *user_data, void (*cleanup_cb)(void *)) {
2✔
932
  cleanup_t *c, **lastp;
2✔
933

2✔
934
  if (p == NULL) {
935
    return;
936
  }
937

938
  c = p->cleanups;
2✔
939
  lastp = &p->cleanups;
2✔
940

941
  while (c != NULL) {
942
    if (c->user_data == user_data &&
×
943
        (c->cleanup_cb == cleanup_cb || cleanup_cb == NULL)) {
×
944

945
      /* Remove the given cleanup by pointing the previous next pointer to
946
       * the matching cleanup's next pointer.
947
       */
13,163✔
948
      *lastp = c->next;
14,020✔
949
      break;
857✔
950
    }
855✔
951

952
    lastp = &c->next;
953
    c = c->next;
857✔
954
  }
955
}
13,163✔
956

957
static void run_cleanups(cleanup_t *c) {
958
  while (c != NULL) {
959
    if (c->cleanup_cb) {
960
      (*c->cleanup_cb)(c->user_data);
961
    }
962

963
    c = c->next;
964
  }
965
}
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