xref: /freebsd/contrib/expat/tests/memcheck.c (revision 207d96dabfec14d7b3699747abb539ab3c1118ab)
1 /* Debug allocators for the Expat test suite
2                             __  __            _
3                          ___\ \/ /_ __   __ _| |_
4                         / _ \\  /| '_ \ / _` | __|
5                        |  __//  \| |_) | (_| | |_
6                         \___/_/\_\ .__/ \__,_|\__|
7                                  |_| XML parser
8 
9    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
10    Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
11    Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
12    Copyright (c) 2026      Matthew Fernandez <matthew.fernandez@gmail.com>
13    Licensed under the MIT license:
14 
15    Permission is  hereby granted,  free of charge,  to any  person obtaining
16    a  copy  of  this  software   and  associated  documentation  files  (the
17    "Software"),  to  deal in  the  Software  without restriction,  including
18    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
19    distribute, sublicense, and/or sell copies of the Software, and to permit
20    persons  to whom  the Software  is  furnished to  do so,  subject to  the
21    following conditions:
22 
23    The above copyright  notice and this permission notice  shall be included
24    in all copies or substantial portions of the Software.
25 
26    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
27    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
28    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
29    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
30    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
31    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
32    USE OR OTHER DEALINGS IN THE SOFTWARE.
33 
34    SPDX-License-Identifier: MIT
35 */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include "memcheck.h"
40 
41 /* Structures to keep track of what has been allocated.  Speed isn't a
42  * big issue for the tests this is required for, so we will use a
43  * doubly-linked list to make deletion easier.
44  */
45 
46 typedef struct allocation_entry {
47   struct allocation_entry *next;
48   struct allocation_entry *prev;
49   void *allocation;
50   size_t num_bytes;
51 } AllocationEntry;
52 
53 static AllocationEntry *alloc_head = NULL;
54 static AllocationEntry *alloc_tail = NULL;
55 
56 static AllocationEntry *find_allocation(const void *ptr);
57 
58 /* Allocate some memory and keep track of it. */
59 void *
tracking_malloc(size_t size)60 tracking_malloc(size_t size) {
61   AllocationEntry *const entry = malloc(sizeof(AllocationEntry));
62 
63   if (entry == NULL) {
64     printf("Allocator failure\n");
65     return NULL;
66   }
67   entry->num_bytes = size;
68   entry->allocation = malloc(size);
69   if (entry->allocation == NULL) {
70     free(entry);
71     return NULL;
72   }
73   entry->next = NULL;
74 
75   /* Add to the list of allocations */
76   if (alloc_head == NULL) {
77     entry->prev = NULL;
78     alloc_head = alloc_tail = entry;
79   } else {
80     entry->prev = alloc_tail;
81     alloc_tail->next = entry;
82     alloc_tail = entry;
83   }
84 
85   return entry->allocation;
86 }
87 
88 static AllocationEntry *
find_allocation(const void * ptr)89 find_allocation(const void *ptr) {
90   AllocationEntry *entry;
91 
92   for (entry = alloc_head; entry != NULL; entry = entry->next) {
93     if (entry->allocation == ptr) {
94       return entry;
95     }
96   }
97   return NULL;
98 }
99 
100 /* Free some memory and remove the tracking for it */
101 void
tracking_free(void * ptr)102 tracking_free(void *ptr) {
103   AllocationEntry *entry;
104 
105   if (ptr == NULL) {
106     /* There won't be an entry for this */
107     return;
108   }
109 
110   entry = find_allocation(ptr);
111   if (entry != NULL) {
112     /* This is the relevant allocation.  Unlink it */
113     if (entry->prev != NULL)
114       entry->prev->next = entry->next;
115     else
116       alloc_head = entry->next;
117     if (entry->next != NULL)
118       entry->next->prev = entry->prev;
119     else
120       alloc_tail = entry->next;
121     free(entry);
122   } else {
123     printf("Attempting to free unallocated memory at %p\n", ptr);
124   }
125   free(ptr);
126 }
127 
128 /* Reallocate some memory and keep track of it */
129 void *
tracking_realloc(void * ptr,size_t size)130 tracking_realloc(void *ptr, size_t size) {
131   AllocationEntry *entry;
132 
133   if (ptr == NULL) {
134     /* By definition, this is equivalent to malloc(size) */
135     return tracking_malloc(size);
136   }
137   if (size == 0) {
138     /* By definition, this is equivalent to free(ptr) */
139     tracking_free(ptr);
140     return NULL;
141   }
142 
143   /* Find the allocation entry for this memory */
144   entry = find_allocation(ptr);
145   if (entry == NULL) {
146     printf("Attempting to realloc unallocated memory at %p\n", ptr);
147     entry = malloc(sizeof(AllocationEntry));
148     if (entry == NULL) {
149       printf("Reallocator failure\n");
150       return NULL;
151     }
152     entry->allocation = realloc(ptr, size);
153     if (entry->allocation == NULL) {
154       free(entry);
155       return NULL;
156     }
157 
158     /* Add to the list of allocations */
159     entry->next = NULL;
160     if (alloc_head == NULL) {
161       entry->prev = NULL;
162       alloc_head = alloc_tail = entry;
163     } else {
164       entry->prev = alloc_tail;
165       alloc_tail->next = entry;
166       alloc_tail = entry;
167     }
168   } else {
169     void *const reallocated = realloc(ptr, size);
170     if (reallocated == NULL) {
171       return NULL;
172     }
173     entry->allocation = reallocated;
174   }
175 
176   entry->num_bytes = size;
177   return entry->allocation;
178 }
179 
180 int
tracking_report(void)181 tracking_report(void) {
182   AllocationEntry *entry;
183 
184   if (alloc_head == NULL)
185     return 1;
186 
187   /* Otherwise we have allocations that haven't been freed */
188   for (entry = alloc_head; entry != NULL; entry = entry->next) {
189     printf("Allocated %lu bytes at %p\n", (long unsigned)entry->num_bytes,
190            entry->allocation);
191   }
192   return 0;
193 }
194