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