Coverage Report

Created: 2025-11-15 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/mempool-datastack.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "mempool.h"
5
6
/*
7
 * The datastack pool is a thin wrapper around the datastack API.  It exists
8
 * to allow datastack allocations via the pool API.
9
 *
10
 * Note: Do not confuse it with the *unsafe* datastack pool.
11
 *
12
 * Implementation
13
 * ==============
14
 *
15
 * A datastack pool maintains information about the datastack frame that was
16
 * in use when the pool was created so it can sanity check all p_new(),
17
 * p_malloc(), and p_realloc() calls.
18
 *
19
 * Creation
20
 * --------
21
 *
22
 * When a datastack pool is created, a new pool structure is allocated from
23
 * the datastack (via t_new()).  The current datastack frame number is saved
24
 * into the pool's private data (struct datastack_pool).
25
 *
26
 * Allocation & Reallocation
27
 * -------------------------
28
 *
29
 * After verifying that the saved datastack frame id matches the currently
30
 * active one, the p_malloc() and p_realloc() calls get directed to
31
 * t_malloc0() and t_try_realloc(), respectively.  There is no
32
 * per-allocation information to track.
33
 *
34
 * Freeing
35
 * -------
36
 *
37
 * Freeing is a no-op unless the currently active data stack frame id is
38
 * different from the one saved during pool creation, in which case the
39
 * process panics.
40
 *
41
 * Clearing
42
 * --------
43
 *
44
 * A no-op.
45
 *
46
 * Destruction
47
 * -----------
48
 *
49
 * Since the memory backing the pool structure itself is allocated from the
50
 * datastack via t_new(), the pool and all allocations it made are freed
51
 * when the datastack frame is popped.
52
 *
53
 * Even though the pool maintains a reference count, no memory is freed when
54
 * it reaches zero.  Once the reference count reaches zero, the state of the
55
 * pool is undefined and none of its memory maybe be used.
56
 */
57
58
static const char *pool_data_stack_get_name(pool_t pool);
59
static void pool_data_stack_ref(pool_t pool);
60
static void pool_data_stack_unref(pool_t *pool);
61
static void *pool_data_stack_malloc(pool_t pool, size_t size);
62
static void pool_data_stack_free(pool_t pool, void *mem);
63
static void *pool_data_stack_realloc(pool_t pool, void *mem,
64
             size_t old_size, size_t new_size);
65
static void pool_data_stack_clear(pool_t pool);
66
static size_t pool_data_stack_get_max_easy_alloc_size(pool_t pool);
67
68
static struct pool_vfuncs static_data_stack_pool_vfuncs = {
69
  pool_data_stack_get_name,
70
71
  pool_data_stack_ref,
72
  pool_data_stack_unref,
73
74
  pool_data_stack_malloc,
75
  pool_data_stack_free,
76
77
  pool_data_stack_realloc,
78
79
  pool_data_stack_clear,
80
  pool_data_stack_get_max_easy_alloc_size
81
};
82
83
static const struct pool static_data_stack_pool = {
84
  .v = &static_data_stack_pool_vfuncs,
85
86
  .alloconly_pool = TRUE,
87
  .datastack_pool = TRUE
88
};
89
90
struct datastack_pool {
91
  struct pool pool;
92
  int refcount;
93
94
  unsigned int data_stack_frame;
95
};
96
97
pool_t pool_datastack_create(void)
98
1
{
99
1
  struct datastack_pool *dpool;
100
101
1
  dpool = t_new(struct datastack_pool, 1);
102
1
  dpool->pool = static_data_stack_pool;
103
1
  dpool->refcount = 1;
104
1
  dpool->data_stack_frame = data_stack_frame_id;
105
1
  return &dpool->pool;
106
1
}
107
108
static const char *pool_data_stack_get_name(pool_t pool ATTR_UNUSED)
109
0
{
110
0
  return "data stack";
111
0
}
112
113
static void pool_data_stack_ref(pool_t pool)
114
0
{
115
0
  struct datastack_pool *dpool =
116
0
    container_of(pool, struct datastack_pool, pool);
117
118
0
  if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
119
0
    i_panic("pool_data_stack_ref(): stack frame changed");
120
121
0
  dpool->refcount++;
122
0
}
123
124
static void pool_data_stack_unref(pool_t *pool)
125
0
{
126
0
  struct datastack_pool *dpool =
127
0
    container_of(*pool, struct datastack_pool, pool);
128
129
0
  if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
130
0
    i_panic("pool_data_stack_unref(): stack frame changed");
131
132
0
  dpool->refcount--;
133
0
  i_assert(dpool->refcount >= 0);
134
135
0
  *pool = NULL;
136
0
}
137
138
static void *pool_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size)
139
2
{
140
2
  struct datastack_pool *dpool =
141
2
    container_of(pool, struct datastack_pool, pool);
142
143
2
  if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
144
0
    i_panic("pool_data_stack_malloc(): stack frame changed");
145
146
2
  return t_malloc0(size);
147
2
}
148
149
static void pool_data_stack_free(pool_t pool, void *mem ATTR_UNUSED)
150
0
{
151
0
  struct datastack_pool *dpool =
152
0
    container_of(pool, struct datastack_pool, pool);
153
154
0
  if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
155
0
    i_panic("pool_data_stack_free(): stack frame changed");
156
0
}
157
158
static void *pool_data_stack_realloc(pool_t pool, void *mem,
159
             size_t old_size, size_t new_size)
160
0
{
161
0
  struct datastack_pool *dpool =
162
0
    container_of(pool, struct datastack_pool, pool);
163
0
  void *new_mem;
164
165
0
  i_assert(old_size < SIZE_MAX);
166
167
  /* @UNSAFE */
168
0
  if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
169
0
    i_panic("pool_data_stack_realloc(): stack frame changed");
170
171
0
  if (old_size >= new_size)
172
0
    return mem;
173
174
0
  if (!t_try_realloc(mem, new_size)) {
175
0
    new_mem = t_malloc_no0(new_size);
176
0
    if (old_size > 0)
177
0
      memcpy(new_mem, mem, old_size);
178
0
    mem = new_mem;
179
0
  }
180
181
0
  memset((char *) mem + old_size, 0, new_size - old_size);
182
0
  return mem;
183
0
}
184
185
static void pool_data_stack_clear(pool_t pool ATTR_UNUSED)
186
0
{
187
0
}
188
189
static size_t
190
pool_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED)
191
0
{
192
0
  return t_get_bytes_available();
193
0
}