xref: /openbsd/lib/libexpat/tests/memcheck.c (revision bd8f1dc3)
12e724bc9Sbluhm /* Debug allocators for the Expat test suite
22e724bc9Sbluhm                             __  __            _
32e724bc9Sbluhm                          ___\ \/ /_ __   __ _| |_
42e724bc9Sbluhm                         / _ \\  /| '_ \ / _` | __|
52e724bc9Sbluhm                        |  __//  \| |_) | (_| | |_
62e724bc9Sbluhm                         \___/_/\_\ .__/ \__,_|\__|
72e724bc9Sbluhm                                  |_| XML parser
82e724bc9Sbluhm 
908819b41Sbluhm    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
10*bd8f1dc3Sbluhm    Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
11*bd8f1dc3Sbluhm    Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
122e724bc9Sbluhm    Licensed under the MIT license:
132e724bc9Sbluhm 
142e724bc9Sbluhm    Permission is  hereby granted,  free of charge,  to any  person obtaining
152e724bc9Sbluhm    a  copy  of  this  software   and  associated  documentation  files  (the
162e724bc9Sbluhm    "Software"),  to  deal in  the  Software  without restriction,  including
172e724bc9Sbluhm    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
182e724bc9Sbluhm    distribute, sublicense, and/or sell copies of the Software, and to permit
192e724bc9Sbluhm    persons  to whom  the Software  is  furnished to  do so,  subject to  the
202e724bc9Sbluhm    following conditions:
212e724bc9Sbluhm 
222e724bc9Sbluhm    The above copyright  notice and this permission notice  shall be included
232e724bc9Sbluhm    in all copies or substantial portions of the Software.
242e724bc9Sbluhm 
252e724bc9Sbluhm    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
262e724bc9Sbluhm    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
272e724bc9Sbluhm    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
282e724bc9Sbluhm    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
292e724bc9Sbluhm    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
302e724bc9Sbluhm    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
312e724bc9Sbluhm    USE OR OTHER DEALINGS IN THE SOFTWARE.
3233ab7b2bSbluhm */
3333ab7b2bSbluhm 
3433ab7b2bSbluhm #include <stdio.h>
3533ab7b2bSbluhm #include <stdlib.h>
3633ab7b2bSbluhm #include "memcheck.h"
3733ab7b2bSbluhm 
3833ab7b2bSbluhm /* Structures to keep track of what has been allocated.  Speed isn't a
3933ab7b2bSbluhm  * big issue for the tests this is required for, so we will use a
4033ab7b2bSbluhm  * doubly-linked list to make deletion easier.
4133ab7b2bSbluhm  */
4233ab7b2bSbluhm 
4333ab7b2bSbluhm typedef struct allocation_entry {
4433ab7b2bSbluhm   struct allocation_entry *next;
4533ab7b2bSbluhm   struct allocation_entry *prev;
4633ab7b2bSbluhm   void *allocation;
4733ab7b2bSbluhm   size_t num_bytes;
4833ab7b2bSbluhm } AllocationEntry;
4933ab7b2bSbluhm 
5033ab7b2bSbluhm static AllocationEntry *alloc_head = NULL;
5133ab7b2bSbluhm static AllocationEntry *alloc_tail = NULL;
5233ab7b2bSbluhm 
53*bd8f1dc3Sbluhm static AllocationEntry *find_allocation(const void *ptr);
5433ab7b2bSbluhm 
5533ab7b2bSbluhm /* Allocate some memory and keep track of it. */
5633ab7b2bSbluhm void *
tracking_malloc(size_t size)5728ce3119Sbluhm tracking_malloc(size_t size) {
58*bd8f1dc3Sbluhm   AllocationEntry *const entry
59*bd8f1dc3Sbluhm       = (AllocationEntry *)malloc(sizeof(AllocationEntry));
6033ab7b2bSbluhm 
6133ab7b2bSbluhm   if (entry == NULL) {
6233ab7b2bSbluhm     printf("Allocator failure\n");
6333ab7b2bSbluhm     return NULL;
6433ab7b2bSbluhm   }
6533ab7b2bSbluhm   entry->num_bytes = size;
6633ab7b2bSbluhm   entry->allocation = malloc(size);
6733ab7b2bSbluhm   if (entry->allocation == NULL) {
6833ab7b2bSbluhm     free(entry);
6933ab7b2bSbluhm     return NULL;
7033ab7b2bSbluhm   }
7133ab7b2bSbluhm   entry->next = NULL;
7233ab7b2bSbluhm 
7333ab7b2bSbluhm   /* Add to the list of allocations */
7433ab7b2bSbluhm   if (alloc_head == NULL) {
7533ab7b2bSbluhm     entry->prev = NULL;
7633ab7b2bSbluhm     alloc_head = alloc_tail = entry;
7733ab7b2bSbluhm   } else {
7833ab7b2bSbluhm     entry->prev = alloc_tail;
7933ab7b2bSbluhm     alloc_tail->next = entry;
8033ab7b2bSbluhm     alloc_tail = entry;
8133ab7b2bSbluhm   }
8233ab7b2bSbluhm 
8333ab7b2bSbluhm   return entry->allocation;
8433ab7b2bSbluhm }
8533ab7b2bSbluhm 
8633ab7b2bSbluhm static AllocationEntry *
find_allocation(const void * ptr)87*bd8f1dc3Sbluhm find_allocation(const void *ptr) {
8833ab7b2bSbluhm   AllocationEntry *entry;
8933ab7b2bSbluhm 
9033ab7b2bSbluhm   for (entry = alloc_head; entry != NULL; entry = entry->next) {
9133ab7b2bSbluhm     if (entry->allocation == ptr) {
9233ab7b2bSbluhm       return entry;
9333ab7b2bSbluhm     }
9433ab7b2bSbluhm   }
9533ab7b2bSbluhm   return NULL;
9633ab7b2bSbluhm }
9733ab7b2bSbluhm 
9833ab7b2bSbluhm /* Free some memory and remove the tracking for it */
9933ab7b2bSbluhm void
tracking_free(void * ptr)10028ce3119Sbluhm tracking_free(void *ptr) {
10133ab7b2bSbluhm   AllocationEntry *entry;
10233ab7b2bSbluhm 
10333ab7b2bSbluhm   if (ptr == NULL) {
10433ab7b2bSbluhm     /* There won't be an entry for this */
10533ab7b2bSbluhm     return;
10633ab7b2bSbluhm   }
10733ab7b2bSbluhm 
10833ab7b2bSbluhm   entry = find_allocation(ptr);
10933ab7b2bSbluhm   if (entry != NULL) {
11033ab7b2bSbluhm     /* This is the relevant allocation.  Unlink it */
11133ab7b2bSbluhm     if (entry->prev != NULL)
11233ab7b2bSbluhm       entry->prev->next = entry->next;
11333ab7b2bSbluhm     else
11433ab7b2bSbluhm       alloc_head = entry->next;
11533ab7b2bSbluhm     if (entry->next != NULL)
11633ab7b2bSbluhm       entry->next->prev = entry->prev;
11733ab7b2bSbluhm     else
11833ab7b2bSbluhm       alloc_tail = entry->next;
11933ab7b2bSbluhm     free(entry);
12033ab7b2bSbluhm   } else {
12133ab7b2bSbluhm     printf("Attempting to free unallocated memory at %p\n", ptr);
12233ab7b2bSbluhm   }
12333ab7b2bSbluhm   free(ptr);
12433ab7b2bSbluhm }
12533ab7b2bSbluhm 
12633ab7b2bSbluhm /* Reallocate some memory and keep track of it */
12733ab7b2bSbluhm void *
tracking_realloc(void * ptr,size_t size)12828ce3119Sbluhm tracking_realloc(void *ptr, size_t size) {
12933ab7b2bSbluhm   AllocationEntry *entry;
13033ab7b2bSbluhm 
13133ab7b2bSbluhm   if (ptr == NULL) {
13233ab7b2bSbluhm     /* By definition, this is equivalent to malloc(size) */
13333ab7b2bSbluhm     return tracking_malloc(size);
13433ab7b2bSbluhm   }
13533ab7b2bSbluhm   if (size == 0) {
13633ab7b2bSbluhm     /* By definition, this is equivalent to free(ptr) */
13733ab7b2bSbluhm     tracking_free(ptr);
13833ab7b2bSbluhm     return NULL;
13933ab7b2bSbluhm   }
14033ab7b2bSbluhm 
14133ab7b2bSbluhm   /* Find the allocation entry for this memory */
14233ab7b2bSbluhm   entry = find_allocation(ptr);
14333ab7b2bSbluhm   if (entry == NULL) {
14433ab7b2bSbluhm     printf("Attempting to realloc unallocated memory at %p\n", ptr);
145*bd8f1dc3Sbluhm     entry = (AllocationEntry *)malloc(sizeof(AllocationEntry));
14633ab7b2bSbluhm     if (entry == NULL) {
14733ab7b2bSbluhm       printf("Reallocator failure\n");
14833ab7b2bSbluhm       return NULL;
14933ab7b2bSbluhm     }
15033ab7b2bSbluhm     entry->allocation = realloc(ptr, size);
15133ab7b2bSbluhm     if (entry->allocation == NULL) {
15233ab7b2bSbluhm       free(entry);
15333ab7b2bSbluhm       return NULL;
15433ab7b2bSbluhm     }
15533ab7b2bSbluhm 
15633ab7b2bSbluhm     /* Add to the list of allocations */
15733ab7b2bSbluhm     entry->next = NULL;
15833ab7b2bSbluhm     if (alloc_head == NULL) {
15933ab7b2bSbluhm       entry->prev = NULL;
16033ab7b2bSbluhm       alloc_head = alloc_tail = entry;
16133ab7b2bSbluhm     } else {
16233ab7b2bSbluhm       entry->prev = alloc_tail;
16333ab7b2bSbluhm       alloc_tail->next = entry;
16433ab7b2bSbluhm       alloc_tail = entry;
16533ab7b2bSbluhm     }
16633ab7b2bSbluhm   } else {
167*bd8f1dc3Sbluhm     void *const reallocated = realloc(ptr, size);
168*bd8f1dc3Sbluhm     if (reallocated == NULL) {
16933ab7b2bSbluhm       return NULL;
17033ab7b2bSbluhm     }
171*bd8f1dc3Sbluhm     entry->allocation = reallocated;
17233ab7b2bSbluhm   }
17333ab7b2bSbluhm 
17433ab7b2bSbluhm   entry->num_bytes = size;
17533ab7b2bSbluhm   return entry->allocation;
17633ab7b2bSbluhm }
17733ab7b2bSbluhm 
17833ab7b2bSbluhm int
tracking_report(void)17928ce3119Sbluhm tracking_report(void) {
18033ab7b2bSbluhm   AllocationEntry *entry;
18133ab7b2bSbluhm 
18233ab7b2bSbluhm   if (alloc_head == NULL)
18333ab7b2bSbluhm     return 1;
18433ab7b2bSbluhm 
18533ab7b2bSbluhm   /* Otherwise we have allocations that haven't been freed */
18628ce3119Sbluhm   for (entry = alloc_head; entry != NULL; entry = entry->next) {
18728ce3119Sbluhm     printf("Allocated %lu bytes at %p\n", (long unsigned)entry->num_bytes,
18828ce3119Sbluhm            entry->allocation);
18933ab7b2bSbluhm   }
19033ab7b2bSbluhm   return 0;
19133ab7b2bSbluhm }
192