Coverage Report

Created: 2025-06-22 06:56

/src/lvm2/libdm/mm/dbg_malloc.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
3
 * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
4
 *
5
 * This file is part of the device-mapper userspace tools.
6
 *
7
 * This copyrighted material is made available to anyone wishing to use,
8
 * modify, copy, or redistribute it subject to the terms and conditions
9
 * of the GNU Lesser General Public License v.2.1.
10
 *
11
 * You should have received a copy of the GNU Lesser General Public License
12
 * along with this program; if not, write to the Free Software Foundation,
13
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
14
 */
15
16
#include "libdm/misc/dmlib.h"
17
18
#ifdef VALGRIND_POOL
19
#include <memcheck.h>
20
#endif
21
#include <assert.h>
22
#include <stdarg.h>
23
24
void *dm_malloc_aux(size_t s, const char *file, int line)
25
        __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
26
void *dm_malloc_aux_debug(size_t s, const char *file, int line)
27
        __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
28
static void *_dm_malloc_aligned_aux(size_t s, size_t a, const char *file, int line)
29
        __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
30
void *dm_zalloc_aux(size_t s, const char *file, int line)
31
        __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
32
void *dm_zalloc_aux_debug(size_t s, const char *file, int line)
33
        __attribute__((__malloc__)) __attribute__((__warn_unused_result__));
34
void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
35
        __attribute__((__warn_unused_result__));
36
void dm_free_aux(void *p);
37
char *dm_strdup_aux(const char *str, const char *file, int line)
38
        __attribute__((__warn_unused_result__));
39
int dm_dump_memory_debug(void);
40
void dm_bounds_check_debug(void);
41
42
char *dm_strdup_aux(const char *str, const char *file, int line)
43
0
{
44
0
  char *ret;
45
0
  size_t len;
46
47
0
  if (!str) {
48
0
    log_error(INTERNAL_ERROR "dm_strdup called with NULL pointer");
49
0
    return NULL;
50
0
  }
51
52
0
  len = strlen(str) + 1;
53
0
  if ((ret = dm_malloc_aux_debug(len, file, line)))
54
0
    memcpy(ret, str, len);
55
56
0
  return ret;
57
0
}
58
59
struct memblock {
60
  struct memblock *prev, *next; /* All allocated blocks are linked */
61
  size_t length;    /* Size of the requested block */
62
  int id;     /* Index of the block */
63
  const char *file; /* File that allocated */
64
  int line;   /* Line that allocated */
65
  void *magic;    /* Address of this block */
66
} __attribute__((aligned(8)));
67
68
static struct {
69
  unsigned block_serialno;/* Non-decreasing serialno of block */
70
  unsigned blocks_allocated; /* Current number of blocks allocated */
71
  unsigned blocks_max;  /* Max no of concurrently-allocated blocks */
72
  unsigned int bytes, mbytes;
73
74
} _mem_stats = {
75
0, 0, 0, 0, 0};
76
77
static struct memblock *_head = 0;
78
static struct memblock *_tail = 0;
79
80
void *dm_malloc_aux_debug(size_t s, const char *file, int line)
81
0
{
82
0
  struct memblock *nb;
83
0
  size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
84
85
0
  if (s > 50000000) {
86
0
    log_error("Huge memory allocation (size %" PRIsize_t
87
0
        ") rejected - metadata corruption?", s);
88
0
    return 0;
89
0
  }
90
91
0
  if (!(nb = malloc(tsize))) {
92
0
    log_error("couldn't allocate any memory, size = %" PRIsize_t,
93
0
        s);
94
0
    return 0;
95
0
  }
96
97
  /* set up the file and line info */
98
0
  nb->file = file;
99
0
  nb->line = line;
100
101
0
  dm_bounds_check();
102
103
  /* setup fields */
104
0
  nb->magic = nb + 1;
105
0
  nb->length = s;
106
0
  nb->id = ++_mem_stats.block_serialno;
107
0
  nb->next = 0;
108
109
  /* stomp a pretty pattern across the new memory
110
     and fill in the boundary bytes */
111
0
  {
112
0
    char *ptr = (char *) (nb + 1);
113
0
    size_t i;
114
0
    for (i = 0; i < s; i++)
115
0
      *ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
116
117
0
    for (i = 0; i < sizeof(unsigned long); i++)
118
0
      *ptr++ = (char) nb->id;
119
0
  }
120
121
0
  nb->prev = _tail;
122
123
  /* link to tail of the list */
124
0
  if (!_head)
125
0
    _head = _tail = nb;
126
0
  else {
127
0
    _tail->next = nb;
128
0
    _tail = nb;
129
0
  }
130
131
0
  _mem_stats.blocks_allocated++;
132
0
  if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)
133
0
    _mem_stats.blocks_max = _mem_stats.blocks_allocated;
134
135
0
  _mem_stats.bytes += s;
136
0
  if (_mem_stats.bytes > _mem_stats.mbytes)
137
0
    _mem_stats.mbytes = _mem_stats.bytes;
138
139
  /* log_debug_mem("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
140
      _mem_stats.bytes); */
141
#ifdef VALGRIND_POOL
142
  VALGRIND_MAKE_MEM_UNDEFINED(nb + 1, s);
143
#endif
144
0
  return nb + 1;
145
0
}
146
147
void *dm_zalloc_aux_debug(size_t s, const char *file, int line)
148
0
{
149
0
  void *ptr = dm_malloc_aux_debug(s, file, line);
150
151
0
  if (ptr)
152
0
    memset(ptr, 0, s);
153
154
0
  return ptr;
155
0
}
156
157
void dm_free_aux(void *p)
158
0
{
159
0
  char *ptr;
160
0
  size_t i;
161
0
  struct memblock *mb = ((struct memblock *) p) - 1;
162
0
  if (!p)
163
0
    return;
164
165
0
  dm_bounds_check();
166
167
  /* sanity check */
168
0
  assert(mb->magic == p);
169
#ifdef VALGRIND_POOL
170
  VALGRIND_MAKE_MEM_DEFINED(p, mb->length);
171
#endif
172
  /* check data at the far boundary */
173
0
  ptr = (char *) p + mb->length;
174
0
  for (i = 0; i < sizeof(unsigned long); i++)
175
0
    if (ptr[i] != (char) mb->id)
176
0
      assert(!"Damage at far end of block");
177
178
  /* have we freed this before ? */
179
0
  assert(mb->id != 0);
180
181
  /* unlink */
182
0
  if (mb->prev)
183
0
    mb->prev->next = mb->next;
184
0
  else
185
0
    _head = mb->next;
186
187
0
  if (mb->next)
188
0
    mb->next->prev = mb->prev;
189
0
  else
190
0
    _tail = mb->prev;
191
192
0
  mb->id = 0;
193
194
  /* stomp a different pattern across the memory */
195
0
  ptr = p;
196
0
  for (i = 0; i < mb->length; i++)
197
0
    ptr[i] = i & 1 ? (char) 0xde : (char) 0xad;
198
199
0
  assert(_mem_stats.blocks_allocated);
200
0
  _mem_stats.blocks_allocated--;
201
0
  _mem_stats.bytes -= mb->length;
202
203
  /* free the memory */
204
0
  free(mb);
205
0
}
206
207
void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
208
0
{
209
0
  void *r;
210
0
  struct memblock *mb = ((struct memblock *) p) - 1;
211
212
0
  r = dm_malloc_aux_debug(s, file, line);
213
214
0
  if (r && p) {
215
0
    memcpy(r, p, mb->length);
216
0
    dm_free_aux(p);
217
0
  }
218
219
0
  return r;
220
0
}
221
222
int dm_dump_memory_debug(void)
223
0
{
224
0
  unsigned long tot = 0;
225
0
  struct memblock *mb;
226
0
  char str[32];
227
228
0
  if (_head)
229
0
    log_very_verbose("You have a memory leak:");
230
231
0
  for (mb = _head; mb; mb = mb->next) {
232
#ifdef VALGRIND_POOL
233
    /*
234
     * We can't look at the memory in case it has had
235
     * VALGRIND_MAKE_MEM_NOACCESS called on it.
236
     */
237
    str[0] = '\0';
238
#else
239
0
    size_t c;
240
241
0
    for (c = 0; c < sizeof(str) - 1; c++) {
242
0
      if (c >= mb->length)
243
0
        str[c] = ' ';
244
0
      else if (((char *)mb->magic)[c] == '\0')
245
0
        str[c] = '\0';
246
0
      else if (((char *)mb->magic)[c] < ' ')
247
0
        str[c] = '?';
248
0
      else
249
0
        str[c] = ((char *)mb->magic)[c];
250
0
    }
251
0
    str[sizeof(str) - 1] = '\0';
252
0
#endif
253
254
0
    LOG_MESG(_LOG_INFO, mb->file, mb->line, 0,
255
0
       "block %d at %p, size %" PRIsize_t "\t [%s]",
256
0
       mb->id, mb->magic, mb->length, str);
257
0
    tot += mb->length;
258
0
  }
259
260
0
  if (_head)
261
0
    log_very_verbose("%ld bytes leaked in total", tot);
262
263
0
  return 1;
264
0
}
265
266
void dm_bounds_check_debug(void)
267
0
{
268
0
  struct memblock *mb = _head;
269
0
  while (mb) {
270
0
    size_t i;
271
0
    char *ptr = ((char *) (mb + 1)) + mb->length;
272
0
    for (i = 0; i < sizeof(unsigned long); i++)
273
0
      if (*ptr++ != (char) mb->id)
274
0
        assert(!"Memory smash");
275
276
0
    mb = mb->next;
277
0
  }
278
0
}
279
280
void *dm_malloc_aux(size_t s, const char *file __attribute__((unused)),
281
        int line __attribute__((unused)))
282
0
{
283
0
  if (s > 50000000) {
284
0
    log_error("Huge memory allocation (size %" PRIsize_t
285
0
        ") rejected - metadata corruption?", s);
286
0
    return 0;
287
0
  }
288
289
0
  return malloc(s);
290
0
}
291
292
/* Allocate size s with alignment a (or page size if 0) */
293
static void *_dm_malloc_aligned_aux(size_t s, size_t a, const char *file __attribute__((unused)),
294
            int line __attribute__((unused)))
295
0
{
296
0
  void *memptr;
297
0
  int r;
298
299
0
  if (!a)
300
0
    a = getpagesize();
301
302
0
  if (s > 50000000) {
303
0
    log_error("Huge memory allocation (size %" PRIsize_t
304
0
        ") rejected - metadata corruption?", s);
305
0
    return 0;
306
0
  }
307
308
0
  if ((r = posix_memalign(&memptr, a, s))) {
309
0
    log_error("Failed to allocate %" PRIsize_t " bytes aligned to %" PRIsize_t ": %s", s, a, strerror(r));
310
0
    return 0;
311
0
  }
312
313
0
  return memptr;
314
0
}
315
316
void *dm_zalloc_aux(size_t s, const char *file, int line)
317
0
{
318
0
  void *ptr = dm_malloc_aux(s, file, line);
319
320
0
  if (ptr)
321
0
    memset(ptr, 0, s);
322
323
0
  return ptr;
324
0
}
325
326
#ifdef DEBUG_MEM
327
328
void *dm_malloc_wrapper(size_t s, const char *file, int line)
329
{
330
  return dm_malloc_aux_debug(s, file, line);
331
}
332
333
void *dm_malloc_aligned_wrapper(size_t s, size_t a, const char *file, int line)
334
{
335
  /* FIXME Implement alignment when debugging - currently just ignored */
336
  return _dm_malloc_aux_debug(s, file, line);
337
}
338
339
void *dm_zalloc_wrapper(size_t s, const char *file, int line)
340
{
341
  return dm_zalloc_aux_debug(s, file, line);
342
}
343
344
char *dm_strdup_wrapper(const char *str, const char *file, int line)
345
{
346
  return dm_strdup_aux(str, file, line);
347
}
348
349
void dm_free_wrapper(void *ptr)
350
{
351
  dm_free_aux(ptr);
352
}
353
354
void *dm_realloc_wrapper(void *p, unsigned int s, const char *file, int line)
355
{
356
  return dm_realloc_aux(p, s, file, line);
357
}
358
359
int dm_dump_memory_wrapper(void)
360
{
361
  return dm_dump_memory_debug();
362
}
363
364
void dm_bounds_check_wrapper(void)
365
{
366
  dm_bounds_check_debug();
367
}
368
369
#else /* !DEBUG_MEM */
370
371
void *dm_malloc_wrapper(size_t s, const char *file, int line)
372
0
{
373
0
  return dm_malloc_aux(s, file, line);
374
0
}
375
376
void *dm_malloc_aligned_wrapper(size_t s, size_t a, const char *file, int line)
377
0
{
378
0
  return _dm_malloc_aligned_aux(s, a, file, line);
379
0
}
380
381
void *dm_zalloc_wrapper(size_t s, const char *file, int line)
382
0
{
383
0
  return dm_zalloc_aux(s, file, line);
384
0
}
385
386
char *dm_strdup_wrapper(const char *str,
387
      const char *file __attribute__((unused)),
388
      int line __attribute__((unused)))
389
0
{
390
0
  return strdup(str);
391
0
}
392
393
void dm_free_wrapper(void *ptr)
394
10.9k
{
395
10.9k
  free(ptr);
396
10.9k
}
397
398
void *dm_realloc_wrapper(void *p, unsigned int s, 
399
       const char *file __attribute__((unused)),
400
       int line __attribute__((unused)))
401
0
{
402
0
  return realloc(p, s);
403
0
}
404
405
int dm_dump_memory_wrapper(void)
406
0
{
407
0
  return 1;
408
0
}
409
410
void dm_bounds_check_wrapper(void)
411
0
{
412
0
}
413
414
#endif /* DEBUG_MEM */