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

proftpd / proftpd / 14552756858

19 Apr 2025 08:53PM UTC coverage: 93.03% (+0.4%) from 92.667%
14552756858

push

github

51358 of 55206 relevant lines covered (93.03%)

213.67 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,605✔
101

11,605✔
102
  if (size == 0) {
103
    /* Avoid zero-length malloc(); on non-POSIX systems, the behavior is
11,605✔
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,605✔
115
    null_alloc();
11,605✔
116
  }
×
117

118
  return res;
119
}
11,605✔
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,605✔
126
    (union block_hdr *) smalloc(size + sizeof(union block_hdr));
11,605✔
127

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

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

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

138
#if defined(PR_USE_DEVEL)
139
  /* Debug code */
140

141
  while (free_blk) {
142
    if (free_blk != blok) {
111,318✔
143
      free_blk = free_blk->h.next;
94,871✔
144
      continue;
94,871✔
145
    }
94,871✔
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,447✔
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,356✔
158
   * last block in chain to free blocks we already had.
159
   */
160

161
  union block_hdr *old_free_list = block_freelist;
162

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

168
  block_freelist = blok;
169

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

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

178
  chk_on_blk_list(blok, old_free_list, pool_tag);
179
  blok->h.first_avail = (char *) (blok + 1);
15,366✔
180
  blok->h.next = old_free_list;
15,366✔
181
}
15,366✔
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,217✔
194
  union block_hdr *blok = block_freelist;
17,217✔
195

17,217✔
196
  if (exact == FALSE) {
197
    if (minsz == 0) {
17,217✔
198
      minsz = 1;
16,307✔
199
    }
16,307✔
200

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

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

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

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

11,605✔
219
    lastptr = &blok->h.next;
11,605✔
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,712✔
450

12,712✔
451
  debug_flags = flags;
12,696✔
452
  return 0;
453
}
454

455
void pr_pool_tag(pool *p, const char *tag) {
12,710✔
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
  }
16✔
469

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

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

477
  pr_alarms_block();
16✔
478

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

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

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

12,784✔
492
  pr_alarms_block();
493

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

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

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

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

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

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

908✔
514
  pr_alarms_unblock();
908✔
515

908✔
516
  return new_pool;
517
}
908✔
518

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

908✔
523
  pr_alarms_block();
524

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

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

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

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

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

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

545
  pr_alarms_unblock();
546

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

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

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

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

560
void free_pools(void) {
13,178✔
561
  destroy_pool(permanent_pool);
562
  permanent_pool = NULL;
563
  pool_release_free_block_list();
13,178✔
564
}
565

566
static void clear_pool(struct pool_rec *p) {
567

13,178✔
568
  /* Sanity check. */
569
  if (p == NULL) {
570
    return;
13,178✔
571
  }
13,178✔
572

573
  pr_alarms_block();
574

18,683✔
575
  /* Run through any cleanups. */
5,505✔
576
  run_cleanups(p->cleanups);
577
  p->cleanups = NULL;
578

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

13,178✔
584
  p->sub_pools = NULL;
13,178✔
585

586
  free_blocks(p->first->h.next, p->tag);
13,178✔
587
  p->first->h.next = NULL;
13,178✔
588

589
  p->last = p->first;
590
  p->first->h.first_avail = p->free_first_avail;
13,195✔
591

13,195✔
592
  p->tag = NULL;
593
  pr_alarms_unblock();
594
}
595

13,178✔
596
void destroy_pool(pool *p) {
597
  if (p == NULL) {
13,178✔
598
    return;
12,284✔
599
  }
12,054✔
600

601
  pr_alarms_block();
602

12,284✔
603
  if (p->parent != NULL) {
230✔
604
    if (p->parent->sub_pools == p) {
605
      p->parent->sub_pools = p->sub_next;
606
    }
12,284✔
607

8,408✔
608
    if (p->sub_prev != NULL) {
609
      p->sub_prev->sub_next = p->sub_next;
610
    }
611

13,178✔
612
    if (p->sub_next != NULL) {
13,178✔
613
      p->sub_next->sub_prev = p->sub_prev;
614
    }
13,178✔
615
  }
616

617
  clear_pool(p);
618
  free_blocks(p->first, p->tag);
619

620
  pr_alarms_unblock();
621

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

35,451✔
631
/* Allocation interface...
35,451✔
632
 */
35,451✔
633

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

641
  if (p == NULL) {
642
    errno = EINVAL;
643
    return NULL;
644
  }
35,451✔
645

35,451✔
646
  /* For performance, see if space is available in the most recently
×
647
   * allocated block.
×
648
   */
649

650
  blok = p->last;
35,451✔
651
  if (blok == NULL) {
652
    errno = EINVAL;
35,451✔
653
    return NULL;
654
  }
655

656
  first_avail = blok->h.first_avail;
657

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

668
  new_first_avail = first_avail + sz;
669

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

3,525✔
675
  /* Need a new one that's big enough */
676
  pr_alarms_block();
3,525✔
677

3,525✔
678
  blok = new_block(sz, exact);
679
  p->last->h.next = blok;
3,525✔
680
  p->last = blok;
3,525✔
681

682
  first_avail = blok->h.first_avail;
683
  blok->h.first_avail = sz + (char *) blok->h.first_avail;
35,445✔
684

7,810✔
685
  pr_alarms_unblock();
686
  return (void *) first_avail;
687
}
6✔
688

3✔
689
void *palloc(struct pool_rec *p, size_t sz) {
690
  return alloc_pool(p, sz, FALSE);
691
}
27,243✔
692

27,243✔
693
void *pallocsz(struct pool_rec *p, size_t sz) {
694
  return alloc_pool(p, sz, TRUE);
27,243✔
695
}
×
696

×
697
void *pcalloc(struct pool_rec *p, size_t sz) {
698
  void *res;
699

27,243✔
700
  if (p == NULL) {
27,243✔
701
    errno = EINVAL;
702
    return NULL;
27,243✔
703
  }
704

705
  res = palloc(p, sz);
3✔
706
  memset(res, '\0', sz);
3✔
707

708
  return res;
3✔
709
}
×
710

×
711
void *pcallocsz(struct pool_rec *p, size_t sz) {
712
  void *res;
713

3✔
714
  if (p == NULL) {
3✔
715
    errno = EINVAL;
716
    return NULL;
3✔
717
  }
718

719
  res = pallocsz(p, sz);
720
  memset(res, '\0', sz);
721

393✔
722
  return res;
393✔
723
}
724

393✔
725
/* Array functions */
393✔
726

3✔
727
array_header *make_array(pool *p, unsigned int nelts, size_t elt_size) {
3✔
728
  array_header *res;
729

730
  if (p == NULL ||
390✔
731
      elt_size == 0) {
732
    errno = EINVAL;
390✔
733
    return NULL;
734
  }
735

736
  res = palloc(p, sizeof(array_header));
390✔
737

390✔
738
  if (nelts < 1) {
390✔
739
    nelts = 1;
390✔
740
  }
390✔
741

742
  res->elts = pcalloc(p, nelts * elt_size);
390✔
743
  res->pool = p;
744
  res->elt_size = elt_size;
745
  res->nelts = 0;
5✔
746
  res->nalloc = nelts;
5✔
747

748
  return res;
749
}
750

4✔
751
void clear_array(array_header *arr) {
4✔
752
  if (arr == NULL) {
753
    return;
754
  }
658✔
755

658✔
756
  arr->elts = pcalloc(arr->pool, arr->nalloc * arr->elt_size);
1✔
757
  arr->nelts = 0;
1✔
758
}
759

760
void *push_array(array_header *arr) {
657✔
761
  if (arr == NULL) {
197✔
762
    errno = EINVAL;
763
    return NULL;
197✔
764
  }
197✔
765

197✔
766
  if (arr->nelts == arr->nalloc) {
767
    char *new_data = pcalloc(arr->pool, arr->nalloc * arr->elt_size * 2);
768

657✔
769
    memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
657✔
770
    arr->elts = new_data;
771
    arr->nalloc *= 2;
772
  }
16✔
773

16✔
774
  ++arr->nelts;
775
  return ((char *) arr->elts) + (arr->elt_size * (arr->nelts - 1));
16✔
776
}
16✔
777

6✔
778
int array_cat2(array_header *dst, const array_header *src) {
6✔
779
  size_t elt_size;
780

781
  if (dst == NULL ||
10✔
782
      src == NULL) {
783
    errno = EINVAL;
10✔
784
    return -1;
6✔
785
  }
6✔
786

787
  elt_size = dst->elt_size;
6✔
788

6✔
789
  if (dst->nelts + src->nelts > dst->nalloc) {
790
    size_t new_size;
791
    char *new_data;
792

14✔
793
    new_size = dst->nalloc * 2;
8✔
794
    if (new_size == 0) {
795
      ++new_size;
796
    }
6✔
797

6✔
798
    while ((dst->nelts + src->nelts) > new_size) {
799
      new_size *= 2;
6✔
800
    }
6✔
801

802
    new_data = pcalloc(dst->pool, elt_size * new_size);
803
    memcpy(new_data, dst->elts, dst->nalloc * elt_size);
10✔
804

10✔
805
    dst->elts = new_data;
10✔
806
    dst->nalloc = new_size;
807
  }
10✔
808

809
  memcpy(((char *) dst->elts) + (dst->nelts * elt_size), (char *) src->elts,
810
         elt_size * src->nelts);
10✔
811
  dst->nelts += src->nelts;
9✔
812

9✔
813
  return 0;
814
}
9✔
815

9✔
816
void array_cat(array_header *dst, const array_header *src) {
817
  (void) array_cat2(dst, src);
9✔
818
}
9✔
819

4✔
820
array_header *copy_array(pool *p, const array_header *arr) {
4✔
821
  array_header *res;
822

823
  if (p == NULL ||
5✔
824
      arr == NULL) {
825
    errno = EINVAL;
5✔
826
    return NULL;
5✔
827
  }
828

829
  res = make_array(p, arr->nalloc, arr->elt_size);
5✔
830

5✔
831
  if (arr->nelts > 0) {
832
    memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts);
833
  }
834

5✔
835
  res->nelts = arr->nelts;
5✔
836
  return res;
5✔
837
}
838

5✔
839
/* copy an array that is assumed to consist solely of strings */
5✔
840
array_header *copy_array_str(pool *p, const array_header *arr) {
3✔
841
  register unsigned int i;
3✔
842
  array_header *res;
843

844
  if (p == NULL ||
2✔
845
      arr == NULL) {
846
    errno = EINVAL;
7✔
847
    return NULL;
3✔
848
  }
849

850
  res = copy_array(p, arr);
851

852
  for (i = 0; i < arr->nelts; i++) {
853
    ((char **) res->elts)[i] = pstrdup(p, ((char **) res->elts)[i]);
5✔
854
  }
5✔
855

856
  return res;
5✔
857
}
5✔
858

3✔
859
array_header *copy_array_hdr(pool *p, const array_header *arr) {
3✔
860
  array_header *res;
861

862
  if (p == NULL ||
2✔
863
      arr == NULL) {
864
    errno = EINVAL;
2✔
865
    return NULL;
2✔
866
  }
2✔
867

2✔
868
  res = palloc(p, sizeof(array_header));
2✔
869

870
  res->elts = arr->elts;
2✔
871
  res->pool = p;
872
  res->elt_size = arr->elt_size;
873
  res->nelts = arr->nelts;
8✔
874
  res->nalloc = arr->nelts;                /* Force overflow on push */
875

8✔
876
  return res;
877
}
8✔
878

8✔
879
array_header *append_arrays(pool *p, const array_header *first,
880
    const array_header *second) {
7✔
881
  array_header *res;
7✔
882

883
  if (p == NULL ||
884
      first == NULL ||
1✔
885
      second == NULL) {
886
    errno = EINVAL;
1✔
887
    return NULL;
1✔
888
  }
889

890
  res = copy_array_hdr(p, first);
891

892
  array_cat(res, second);
893
  return res;
894
}
895

896
/* Generic cleanups */
897

898
typedef struct cleanup {
899
  void *user_data;
926✔
900
  void (*cleanup_cb)(void *);
926✔
901
  struct cleanup *next;
902

926✔
903
} cleanup_t;
904

905
void register_cleanup2(pool *p, void *user_data, void (*cleanup_cb)(void*)) {
906
  cleanup_t *c;
924✔
907

924✔
908
  if (p == NULL) {
924✔
909
    return;
910
  }
911

924✔
912
  c = pcalloc(p, sizeof(cleanup_t));
924✔
913
  c->user_data = user_data;
914
  c->cleanup_cb = cleanup_cb;
915

5✔
916
  /* Add this cleanup to the given pool's list of cleanups. */
917
  c->next = p->cleanups;
5✔
918
  p->cleanups = c;
5✔
919
}
5✔
920

921
void register_cleanup(pool *p, void *user_data, void (*plain_cleanup_cb)(void*),
3✔
922
    void (*child_cleanup_cb)(void *)) {
3✔
923
  (void) child_cleanup_cb;
924
  register_cleanup2(p, user_data, plain_cleanup_cb);
3✔
925
}
926

927
void unregister_cleanup(pool *p, void *user_data, void (*cleanup_cb)(void *)) {
928
  cleanup_t *c, **lastp;
2✔
929

2✔
930
  if (p == NULL) {
931
    return;
2✔
932
  }
2✔
933

2✔
934
  c = p->cleanups;
935
  lastp = &p->cleanups;
936

937
  while (c != NULL) {
938
    if (c->user_data == user_data &&
2✔
939
        (c->cleanup_cb == cleanup_cb || cleanup_cb == NULL)) {
2✔
940

941
      /* Remove the given cleanup by pointing the previous next pointer to
942
       * the matching cleanup's next pointer.
×
943
       */
×
944
      *lastp = c->next;
945
      break;
946
    }
947

13,178✔
948
    lastp = &c->next;
14,036✔
949
    c = c->next;
858✔
950
  }
856✔
951
}
952

953
static void run_cleanups(cleanup_t *c) {
858✔
954
  while (c != NULL) {
955
    if (c->cleanup_cb) {
13,178✔
956
      (*c->cleanup_cb)(c->user_data);
957
    }
958

959
    c = c->next;
960
  }
961
}
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