Coverage Report

Created: 2026-06-13 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/xmlmemory.c
Line
Count
Source
1
/*
2
 * xmlmemory.c:  libxml memory allocator wrapper.
3
 *
4
 * Author: Daniel Veillard
5
 */
6
7
#define IN_LIBXML
8
#include "libxml.h"
9
10
#include <string.h>
11
#include <stdlib.h>
12
#include <ctype.h>
13
#include <time.h>
14
15
#include <libxml/xmlmemory.h>
16
#include <libxml/xmlerror.h>
17
#include <libxml/parser.h>
18
#include <libxml/threads.h>
19
20
#include "private/error.h"
21
#include "private/memory.h"
22
#include "private/threads.h"
23
24
static unsigned long  debugMemSize = 0;
25
static unsigned long  debugMemBlocks = 0;
26
static xmlMutex xmlMemMutex;
27
28
/************************************************************************
29
 *                  *
30
 *    Macros, variables and associated types      *
31
 *                  *
32
 ************************************************************************/
33
34
/*
35
 * Each of the blocks allocated begin with a header containing information
36
 */
37
38
0
#define MEMTAG 0x5aa5U
39
40
typedef struct memnod {
41
    unsigned int   mh_tag;
42
    size_t         mh_size;
43
} MEMHDR;
44
45
#ifdef SUN4
46
#define ALIGN_SIZE  16
47
#else
48
0
#define ALIGN_SIZE  sizeof(double)
49
#endif
50
0
#define RESERVE_SIZE (((sizeof(MEMHDR) + ALIGN_SIZE - 1) \
51
0
          / ALIGN_SIZE ) * ALIGN_SIZE)
52
53
0
#define MAX_SIZE_T ((size_t)-1)
54
55
0
#define CLIENT_2_HDR(a) ((void *) (((char *) (a)) - RESERVE_SIZE))
56
0
#define HDR_2_CLIENT(a)    ((void *) (((char *) (a)) + RESERVE_SIZE))
57
58
/**
59
 * @deprecated don't use
60
 *
61
 * @param size  an int specifying the size in byte to allocate.
62
 * @param file  the file name or NULL
63
 * @param line  the line number
64
 * @returns a pointer to the allocated area or NULL in case of lack of memory.
65
 */
66
void *
67
xmlMallocLoc(size_t size, const char *file ATTRIBUTE_UNUSED,
68
             int line ATTRIBUTE_UNUSED)
69
0
{
70
0
    return(xmlMemMalloc(size));
71
0
}
72
73
/**
74
 * @deprecated don't use
75
 *
76
 * @param size  an unsigned int specifying the size in byte to allocate.
77
 * @param file  the file name or NULL
78
 * @param line  the line number
79
 * @returns a pointer to the allocated area or NULL in case of lack of memory.
80
 */
81
void *
82
xmlMallocAtomicLoc(size_t size, const char *file ATTRIBUTE_UNUSED,
83
                   int line ATTRIBUTE_UNUSED)
84
0
{
85
0
    return(xmlMemMalloc(size));
86
0
}
87
88
/**
89
 * a malloc() equivalent, with logging of the allocation info.
90
 *
91
 * @param size  an int specifying the size in byte to allocate.
92
 * @returns a pointer to the allocated area or NULL in case of lack of memory.
93
 */
94
void *
95
xmlMemMalloc(size_t size)
96
0
{
97
0
    MEMHDR *p;
98
99
0
    xmlInitParser();
100
101
0
    if (size > (MAX_SIZE_T - RESERVE_SIZE))
102
0
        return(NULL);
103
104
0
    p = (MEMHDR *) malloc(RESERVE_SIZE + size);
105
0
    if (!p)
106
0
        return(NULL);
107
0
    p->mh_tag = MEMTAG;
108
0
    p->mh_size = size;
109
110
0
    xmlMutexLock(&xmlMemMutex);
111
0
    debugMemSize += size;
112
0
    debugMemBlocks++;
113
0
    xmlMutexUnlock(&xmlMemMutex);
114
115
0
    return(HDR_2_CLIENT(p));
116
0
}
117
118
/**
119
 * @deprecated don't use
120
 *
121
 * @param ptr  the initial memory block pointer
122
 * @param size  an int specifying the size in byte to allocate.
123
 * @param file  the file name or NULL
124
 * @param line  the line number
125
 * @returns a pointer to the allocated area or NULL in case of lack of memory.
126
 */
127
void *
128
xmlReallocLoc(void *ptr, size_t size, const char *file ATTRIBUTE_UNUSED,
129
              int line ATTRIBUTE_UNUSED)
130
0
{
131
0
    return(xmlMemRealloc(ptr, size));
132
0
}
133
134
/**
135
 * a realloc() equivalent, with logging of the allocation info.
136
 *
137
 * @param ptr  the initial memory block pointer
138
 * @param size  an int specifying the size in byte to allocate.
139
 * @returns a pointer to the allocated area or NULL in case of lack of memory.
140
 */
141
void *
142
0
xmlMemRealloc(void *ptr, size_t size) {
143
0
    MEMHDR *p, *tmp;
144
0
    size_t oldSize;
145
146
0
    if (ptr == NULL)
147
0
        return(xmlMemMalloc(size));
148
149
0
    xmlInitParser();
150
151
0
    if (size > (MAX_SIZE_T - RESERVE_SIZE))
152
0
        return(NULL);
153
154
0
    p = CLIENT_2_HDR(ptr);
155
0
    if (p->mh_tag != MEMTAG) {
156
0
        xmlPrintErrorMessage("xmlMemRealloc: Tag error\n");
157
0
        return(NULL);
158
0
    }
159
0
    oldSize = p->mh_size;
160
0
    p->mh_tag = ~MEMTAG;
161
162
0
    tmp = (MEMHDR *) realloc(p, RESERVE_SIZE + size);
163
0
    if (!tmp) {
164
0
        p->mh_tag = MEMTAG;
165
0
        return(NULL);
166
0
    }
167
0
    p = tmp;
168
0
    p->mh_tag = MEMTAG;
169
0
    p->mh_size = size;
170
171
0
    xmlMutexLock(&xmlMemMutex);
172
0
    debugMemSize -= oldSize;
173
0
    debugMemSize += size;
174
0
    xmlMutexUnlock(&xmlMemMutex);
175
176
0
    return(HDR_2_CLIENT(p));
177
0
}
178
179
/**
180
 * a free() equivalent, with error checking.
181
 *
182
 * @param ptr  the memory block pointer
183
 */
184
void
185
xmlMemFree(void *ptr)
186
0
{
187
0
    MEMHDR *p;
188
189
0
    if (ptr == NULL)
190
0
        return;
191
192
0
    if (ptr == (void *) -1) {
193
0
        xmlPrintErrorMessage("xmlMemFree: Pointer from freed area\n");
194
0
        return;
195
0
    }
196
197
0
    p = CLIENT_2_HDR(ptr);
198
0
    if (p->mh_tag != MEMTAG) {
199
0
        xmlPrintErrorMessage("xmlMemFree: Tag error\n");
200
0
        return;
201
0
    }
202
0
    p->mh_tag = ~MEMTAG;
203
0
    memset(ptr, -1, p->mh_size);
204
205
0
    xmlMutexLock(&xmlMemMutex);
206
0
    debugMemSize -= p->mh_size;
207
0
    debugMemBlocks--;
208
0
    xmlMutexUnlock(&xmlMemMutex);
209
210
0
    free(p);
211
0
}
212
213
/**
214
 * @deprecated don't use
215
 *
216
 * @param str  the initial string pointer
217
 * @param file  the file name or NULL
218
 * @param line  the line number
219
 * @returns a pointer to the new string or NULL if allocation error occurred.
220
 */
221
char *
222
xmlMemStrdupLoc(const char *str, const char *file ATTRIBUTE_UNUSED,
223
                int line ATTRIBUTE_UNUSED)
224
0
{
225
0
    return(xmlMemoryStrdup(str));
226
0
}
227
228
/**
229
 * a strdup() equivalent, with logging of the allocation info.
230
 *
231
 * @param str  the initial string pointer
232
 * @returns a pointer to the new string or NULL if allocation error occurred.
233
 */
234
char *
235
0
xmlMemoryStrdup(const char *str) {
236
0
    char *s;
237
0
    size_t size = strlen(str) + 1;
238
0
    MEMHDR *p;
239
240
0
    xmlInitParser();
241
242
0
    if (size > (MAX_SIZE_T - RESERVE_SIZE))
243
0
        return(NULL);
244
245
0
    p = (MEMHDR *) malloc(RESERVE_SIZE + size);
246
0
    if (!p)
247
0
        return(NULL);
248
0
    p->mh_tag = MEMTAG;
249
0
    p->mh_size = size;
250
251
0
    xmlMutexLock(&xmlMemMutex);
252
0
    debugMemSize += size;
253
0
    debugMemBlocks++;
254
0
    xmlMutexUnlock(&xmlMemMutex);
255
256
0
    s = (char *) HDR_2_CLIENT(p);
257
258
0
    memcpy(s, str, size);
259
260
0
    return(s);
261
0
}
262
263
/**
264
 * @param ptr  pointer to the memory allocation
265
 * @returns the size of a memory allocation.
266
 */
267
268
size_t
269
0
xmlMemSize(void *ptr) {
270
0
    MEMHDR *p;
271
272
0
    if (ptr == NULL)
273
0
  return(0);
274
275
0
    p = CLIENT_2_HDR(ptr);
276
0
    if (p->mh_tag != MEMTAG)
277
0
        return(0);
278
279
0
    return(p->mh_size);
280
0
}
281
282
/**
283
 * Provides the amount of memory currently allocated
284
 *
285
 * @returns an int representing the amount of memory allocated.
286
 */
287
288
int
289
0
xmlMemUsed(void) {
290
0
    return(debugMemSize);
291
0
}
292
293
/**
294
 * Provides the number of memory areas currently allocated
295
 *
296
 * @returns an int representing the number of blocks
297
 */
298
299
int
300
0
xmlMemBlocks(void) {
301
0
    int res;
302
303
0
    xmlMutexLock(&xmlMemMutex);
304
0
    res = debugMemBlocks;
305
0
    xmlMutexUnlock(&xmlMemMutex);
306
0
    return(res);
307
0
}
308
309
/**
310
 * @deprecated This feature was removed.
311
 * @param fp  a FILE descriptor
312
 * @param nbBytes  the amount of memory to dump
313
 */
314
void
315
xmlMemDisplayLast(FILE *fp ATTRIBUTE_UNUSED, long nbBytes ATTRIBUTE_UNUSED)
316
0
{
317
0
}
318
319
/**
320
 * @deprecated This feature was removed.
321
 * @param fp  a FILE descriptor
322
 */
323
void
324
xmlMemDisplay(FILE *fp ATTRIBUTE_UNUSED)
325
0
{
326
0
}
327
328
/**
329
 * @deprecated This feature was removed.
330
 * @param fp  a FILE descriptor
331
 * @param nr  number of entries to dump
332
 */
333
void
334
xmlMemShow(FILE *fp ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED)
335
0
{
336
0
}
337
338
/**
339
 * @deprecated This feature was removed.
340
 */
341
void
342
xmlMemoryDump(void)
343
0
{
344
0
}
345
346
347
/****************************************************************
348
 *                *
349
 *    Array growth helper       *
350
 *                *
351
 ****************************************************************/
352
353
/**
354
 * Grow an array, combining overflow-checked capacity growth with
355
 * reallocation.
356
 *
357
 * @param array  pointer to the array (may be NULL for first allocation)
358
 * @param elemSize  size of each element in bytes
359
 * @param capacity  pointer to current capacity (updated on success)
360
 * @param min  initial allocation size
361
 * @param max  maximum number of elements
362
 * @returns the new pointer on success, or NULL on failure with the
363
 *          original array and capacity preserved.
364
 */
365
void *
366
0
xmlGrowArray(void *array, size_t elemSize, int *capacity, int min, int max) {
367
0
    void *tmp;
368
0
    int newSize;
369
370
0
    if (capacity == NULL)
371
0
        return(NULL);
372
373
0
    newSize = xmlGrowCapacity(*capacity, elemSize, min, max);
374
0
    if (newSize < 0)
375
0
        return(NULL);
376
377
0
    tmp = xmlRealloc(array, (size_t) newSize * elemSize);
378
0
    if (tmp == NULL)
379
0
        return(NULL);
380
381
0
    *capacity = newSize;
382
0
    return(tmp);
383
0
}
384
385
/****************************************************************
386
 *                *
387
 *    Initialization Routines       *
388
 *                *
389
 ****************************************************************/
390
391
/**
392
 * @deprecated Alias for #xmlInitParser.
393
 *
394
 * @returns 0.
395
 */
396
int
397
0
xmlInitMemory(void) {
398
0
    xmlInitParser();
399
0
    return(0);
400
0
}
401
402
/**
403
 * Initialize the memory layer.
404
 */
405
void
406
1
xmlInitMemoryInternal(void) {
407
1
    xmlInitMutex(&xmlMemMutex);
408
1
}
409
410
/**
411
 * @deprecated This function is a no-op. Call #xmlCleanupParser
412
 * to free global state but see the warnings there. #xmlCleanupParser
413
 * should be only called once at program exit. In most cases, you don't
414
 * have call cleanup functions at all.
415
 */
416
void
417
0
xmlCleanupMemory(void) {
418
0
}
419
420
/**
421
 * Free up all the memory allocated by the library for its own
422
 * use. This should not be called by user level code.
423
 */
424
void
425
0
xmlCleanupMemoryInternal(void) {
426
    /*
427
     * Don't clean up mutex on Windows. Global state destructors can call
428
     * malloc functions after xmlCleanupParser was called. If memory
429
     * debugging is enabled, xmlMemMutex can be used after cleanup.
430
     *
431
     * See python/tests/thread2.py
432
     */
433
0
#if !defined(LIBXML_THREAD_ENABLED) || !defined(_WIN32)
434
0
    xmlCleanupMutex(&xmlMemMutex);
435
0
#endif
436
0
}
437
438
/**
439
 * Override the default memory access functions with a new set
440
 * This has to be called before any other libxml routines !
441
 *
442
 * Should this be blocked if there was already some allocations
443
 * done ?
444
 *
445
 * @param freeFunc  the free() function to use
446
 * @param mallocFunc  the malloc() function to use
447
 * @param reallocFunc  the realloc() function to use
448
 * @param strdupFunc  the strdup() function to use
449
 * @returns 0 on success
450
 */
451
int
452
xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
453
0
            xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
454
0
    if (freeFunc == NULL)
455
0
  return(-1);
456
0
    if (mallocFunc == NULL)
457
0
  return(-1);
458
0
    if (reallocFunc == NULL)
459
0
  return(-1);
460
0
    if (strdupFunc == NULL)
461
0
  return(-1);
462
0
    xmlFree = freeFunc;
463
0
    xmlMalloc = mallocFunc;
464
0
    xmlMallocAtomic = mallocFunc;
465
0
    xmlRealloc = reallocFunc;
466
0
    xmlMemStrdup = strdupFunc;
467
0
    return(0);
468
0
}
469
470
/**
471
 * Provides the memory access functions set currently in use
472
 *
473
 * @param freeFunc  place to save the free() function in use
474
 * @param mallocFunc  place to save the malloc() function in use
475
 * @param reallocFunc  place to save the realloc() function in use
476
 * @param strdupFunc  place to save the strdup() function in use
477
 * @returns 0 on success
478
 */
479
int
480
xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
481
0
    xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) {
482
0
    if (freeFunc != NULL) *freeFunc = xmlFree;
483
0
    if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
484
0
    if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
485
0
    if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
486
0
    return(0);
487
0
}
488
489
/**
490
 * Override the default memory access functions with a new set
491
 * This has to be called before any other libxml routines !
492
 * The mallocAtomicFunc is specialized for atomic block
493
 * allocations (i.e. of areas  useful for garbage collected memory allocators
494
 *
495
 * @deprecated Use #xmlMemSetup.
496
 *
497
 * Should this be blocked if there was already some allocations
498
 * done ?
499
 *
500
 * @param freeFunc  the free() function to use
501
 * @param mallocFunc  the malloc() function to use
502
 * @param mallocAtomicFunc  the malloc() function to use for atomic allocations
503
 * @param reallocFunc  the realloc() function to use
504
 * @param strdupFunc  the strdup() function to use
505
 * @returns 0 on success
506
 */
507
int
508
xmlGcMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
509
              xmlMallocFunc mallocAtomicFunc, xmlReallocFunc reallocFunc,
510
0
        xmlStrdupFunc strdupFunc) {
511
0
    if (freeFunc == NULL)
512
0
  return(-1);
513
0
    if (mallocFunc == NULL)
514
0
  return(-1);
515
0
    if (mallocAtomicFunc == NULL)
516
0
  return(-1);
517
0
    if (reallocFunc == NULL)
518
0
  return(-1);
519
0
    if (strdupFunc == NULL)
520
0
  return(-1);
521
0
    xmlFree = freeFunc;
522
0
    xmlMalloc = mallocFunc;
523
0
    xmlMallocAtomic = mallocAtomicFunc;
524
0
    xmlRealloc = reallocFunc;
525
0
    xmlMemStrdup = strdupFunc;
526
0
    return(0);
527
0
}
528
529
/**
530
 * Provides the memory access functions set currently in use
531
 * The mallocAtomicFunc is specialized for atomic block
532
 * allocations (i.e. of areas  useful for garbage collected memory allocators
533
 *
534
 * @deprecated Use #xmlMemGet.
535
 *
536
 * @param freeFunc  place to save the free() function in use
537
 * @param mallocFunc  place to save the malloc() function in use
538
 * @param mallocAtomicFunc  place to save the atomic malloc() function in use
539
 * @param reallocFunc  place to save the realloc() function in use
540
 * @param strdupFunc  place to save the strdup() function in use
541
 * @returns 0 on success
542
 */
543
int
544
xmlGcMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
545
            xmlMallocFunc *mallocAtomicFunc, xmlReallocFunc *reallocFunc,
546
0
      xmlStrdupFunc *strdupFunc) {
547
0
    if (freeFunc != NULL) *freeFunc = xmlFree;
548
0
    if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
549
0
    if (mallocAtomicFunc != NULL) *mallocAtomicFunc = xmlMallocAtomic;
550
0
    if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
551
0
    if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
552
0
    return(0);
553
0
}
554