mirror of
https://github.com/libexpat/libexpat.git
synced 2025-04-07 22:19:12 +00:00
Test to catch Issue #17
This commit is contained in:
parent
d307e44fbf
commit
ec322eb428
7 changed files with 250 additions and 8 deletions
|
@ -142,12 +142,12 @@ endif(BUILD_examples)
|
|||
|
||||
if(BUILD_tests)
|
||||
## these are unittests that can be run on any platform
|
||||
add_executable(runtests tests/runtests.c tests/chardata.c tests/minicheck.c)
|
||||
add_executable(runtests tests/runtests.c tests/chardata.c tests/minicheck.c tests/memcheck.c)
|
||||
set_property(TARGET runtests PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
|
||||
target_link_libraries(runtests expat)
|
||||
add_test(runtests tests/runtests)
|
||||
|
||||
add_executable(runtestspp tests/runtestspp.cpp tests/chardata.c tests/minicheck.c)
|
||||
add_executable(runtestspp tests/runtestspp.cpp tests/chardata.c tests/minicheck.c tests/memcheck.c)
|
||||
set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
|
||||
target_link_libraries(runtestspp expat)
|
||||
add_test(runtestspp tests/runtestspp)
|
||||
|
|
|
@ -165,12 +165,13 @@ examples/outline: examples/outline.@OBJEXT@ $(LIBRARY)
|
|||
|
||||
tests/chardata.@OBJEXT@: tests/chardata.c tests/chardata.h
|
||||
tests/minicheck.@OBJEXT@: tests/minicheck.c tests/minicheck.h
|
||||
tests/runtests.@OBJEXT@: tests/runtests.c tests/chardata.h
|
||||
tests/runtests: tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
|
||||
$(LINK_EXE) tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
|
||||
tests/runtestspp.@OBJEXT@: tests/runtestspp.cpp tests/runtests.c tests/chardata.h
|
||||
tests/runtestspp: tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
|
||||
$(LINK_CXX_EXE) tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
|
||||
tests/memcheck.@OBJEXT@: tests/memcheck.c tests/memcheck.h
|
||||
tests/runtests.@OBJEXT@: tests/runtests.c tests/chardata.h tests/memcheck.h
|
||||
tests/runtests: tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
|
||||
$(LINK_EXE) tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
|
||||
tests/runtestspp.@OBJEXT@: tests/runtestspp.cpp tests/runtests.c tests/chardata.h tests/memcheck.h
|
||||
tests/runtestspp: tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
|
||||
$(LINK_CXX_EXE) tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
|
||||
|
||||
tests/benchmark/benchmark.@OBJEXT@: tests/benchmark/benchmark.c
|
||||
tests/benchmark/benchmark: tests/benchmark/benchmark.@OBJEXT@ $(LIBRARY)
|
||||
|
|
173
expat/tests/memcheck.c
Normal file
173
expat/tests/memcheck.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/* Copyright (c) 2017 The Expat Maintainers
|
||||
* Copying is permitted under the MIT license. See the file COPYING
|
||||
* for details.
|
||||
*
|
||||
* memcheck.c : debug allocators for the Expat test suite
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "memcheck.h"
|
||||
|
||||
|
||||
/* Structures to keep track of what has been allocated. Speed isn't a
|
||||
* big issue for the tests this is required for, so we will use a
|
||||
* doubly-linked list to make deletion easier.
|
||||
*/
|
||||
|
||||
typedef struct allocation_entry {
|
||||
struct allocation_entry * next;
|
||||
struct allocation_entry * prev;
|
||||
void * allocation;
|
||||
size_t num_bytes;
|
||||
} AllocationEntry;
|
||||
|
||||
static AllocationEntry *alloc_head = NULL;
|
||||
static AllocationEntry *alloc_tail = NULL;
|
||||
|
||||
static AllocationEntry *find_allocation(void *ptr);
|
||||
|
||||
|
||||
/* Allocate some memory and keep track of it. */
|
||||
void *
|
||||
tracking_malloc(size_t size)
|
||||
{
|
||||
AllocationEntry *entry = malloc(sizeof(AllocationEntry));
|
||||
|
||||
if (entry == NULL) {
|
||||
printf("Allocator failure\n");
|
||||
return NULL;
|
||||
}
|
||||
entry->num_bytes = size;
|
||||
entry->allocation = malloc(size);
|
||||
if (entry->allocation == NULL) {
|
||||
free(entry);
|
||||
return NULL;
|
||||
}
|
||||
entry->next = NULL;
|
||||
|
||||
/* Add to the list of allocations */
|
||||
if (alloc_head == NULL) {
|
||||
entry->prev = NULL;
|
||||
alloc_head = alloc_tail = entry;
|
||||
} else {
|
||||
entry->prev = alloc_tail;
|
||||
alloc_tail->next = entry;
|
||||
alloc_tail = entry;
|
||||
}
|
||||
|
||||
return entry->allocation;
|
||||
}
|
||||
|
||||
static AllocationEntry *
|
||||
find_allocation(void *ptr)
|
||||
{
|
||||
AllocationEntry *entry;
|
||||
|
||||
for (entry = alloc_head; entry != NULL; entry = entry->next) {
|
||||
if (entry->allocation == ptr) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free some memory and remove the tracking for it */
|
||||
void
|
||||
tracking_free(void *ptr)
|
||||
{
|
||||
AllocationEntry *entry;
|
||||
|
||||
if (ptr == NULL) {
|
||||
/* There won't be an entry for this */
|
||||
return;
|
||||
}
|
||||
|
||||
entry = find_allocation(ptr);
|
||||
if (entry != NULL) {
|
||||
/* This is the relevant allocation. Unlink it */
|
||||
if (entry->prev != NULL)
|
||||
entry->prev->next = entry->next;
|
||||
else
|
||||
alloc_head = entry->next;
|
||||
if (entry->next != NULL)
|
||||
entry->next->prev = entry->prev;
|
||||
else
|
||||
alloc_tail = entry->next;
|
||||
free(entry);
|
||||
} else {
|
||||
printf("Attempting to free unallocated memory at %p\n", ptr);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/* Reallocate some memory and keep track of it */
|
||||
void *
|
||||
tracking_realloc(void *ptr, size_t size)
|
||||
{
|
||||
AllocationEntry *entry;
|
||||
|
||||
if (ptr == NULL) {
|
||||
/* By definition, this is equivalent to malloc(size) */
|
||||
return tracking_malloc(size);
|
||||
}
|
||||
if (size == 0) {
|
||||
/* By definition, this is equivalent to free(ptr) */
|
||||
tracking_free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the allocation entry for this memory */
|
||||
entry = find_allocation(ptr);
|
||||
if (entry == NULL) {
|
||||
printf("Attempting to realloc unallocated memory at %p\n", ptr);
|
||||
entry = malloc(sizeof(AllocationEntry));
|
||||
if (entry == NULL) {
|
||||
printf("Reallocator failure\n");
|
||||
return NULL;
|
||||
}
|
||||
entry->allocation = realloc(ptr, size);
|
||||
if (entry->allocation == NULL) {
|
||||
free(entry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add to the list of allocations */
|
||||
entry->next = NULL;
|
||||
if (alloc_head == NULL) {
|
||||
entry->prev = NULL;
|
||||
alloc_head = alloc_tail = entry;
|
||||
} else {
|
||||
entry->prev = alloc_tail;
|
||||
alloc_tail->next = entry;
|
||||
alloc_tail = entry;
|
||||
}
|
||||
} else {
|
||||
entry->allocation = realloc(ptr, size);
|
||||
if (entry->allocation == NULL) {
|
||||
/* Realloc semantics say the original is still allocated */
|
||||
entry->allocation = ptr;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
entry->num_bytes = size;
|
||||
return entry->allocation;
|
||||
}
|
||||
|
||||
int
|
||||
tracking_report(void)
|
||||
{
|
||||
AllocationEntry *entry;
|
||||
|
||||
if (alloc_head == NULL)
|
||||
return 1;
|
||||
|
||||
/* Otherwise we have allocations that haven't been freed */
|
||||
for (entry = alloc_head; entry != NULL; entry = entry->next)
|
||||
{
|
||||
printf("Allocated %lu bytes at %p\n",
|
||||
entry->num_bytes, entry->allocation);
|
||||
}
|
||||
return 0;
|
||||
}
|
34
expat/tests/memcheck.h
Normal file
34
expat/tests/memcheck.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (c) 2017 The Expat Maintainers
|
||||
* Copying is permitted under the MIT license. See the file COPYING
|
||||
* for details.
|
||||
*
|
||||
* memcheck.h
|
||||
*
|
||||
* Interface to allocation functions that will track what has or has
|
||||
* not been freed.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef XML_MEMCHECK_H
|
||||
#define XML_MEMCHECK_H 1
|
||||
|
||||
/* Allocation declarations */
|
||||
|
||||
void *tracking_malloc(size_t size);
|
||||
void tracking_free(void *ptr);
|
||||
void *tracking_realloc(void *ptr, size_t size);
|
||||
|
||||
/* End-of-test check to see if unfreed allocations remain. Returns
|
||||
* TRUE (1) if there is nothing, otherwise prints a report of the
|
||||
* remaining allocations and returns FALSE (0).
|
||||
*/
|
||||
int tracking_report(void);
|
||||
|
||||
#endif /* XML_MEMCHECK_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -24,6 +24,7 @@
|
|||
#include "chardata.h"
|
||||
#include "internal.h" /* for UNUSED_P only */
|
||||
#include "minicheck.h"
|
||||
#include "memcheck.h"
|
||||
|
||||
#if defined(__amigaos__) && defined(__USE_INLINE__)
|
||||
#include <proto/expat.h>
|
||||
|
@ -2892,6 +2893,30 @@ START_TEST(test_misc_version)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* Regression test for GitHub Issue #17: memory leak parsing attribute
|
||||
* values with mixed bound and unbound namespaces.
|
||||
*/
|
||||
START_TEST(test_misc_attribute_leak)
|
||||
{
|
||||
const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>";
|
||||
XML_Memory_Handling_Suite memsuite = {
|
||||
tracking_malloc,
|
||||
tracking_realloc,
|
||||
tracking_free
|
||||
};
|
||||
|
||||
parser = XML_ParserCreate_MM("UTF-8", &memsuite, "\n");
|
||||
expect_failure(text, XML_ERROR_UNBOUND_PREFIX,
|
||||
"Unbound prefixes not found");
|
||||
XML_ParserFree(parser);
|
||||
/* Prevent the teardown trying to double free */
|
||||
parser = NULL;
|
||||
|
||||
if (!tracking_report())
|
||||
fail("Memory leak found");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
static void
|
||||
alloc_setup(void)
|
||||
|
@ -3431,6 +3456,7 @@ make_suite(void)
|
|||
tcase_add_test(tc_misc, test_misc_alloc_ns_parse_buffer);
|
||||
tcase_add_test(tc_misc, test_misc_error_string);
|
||||
tcase_add_test(tc_misc, test_misc_version);
|
||||
tcase_add_test(tc_misc, test_misc_attribute_leak);
|
||||
|
||||
suite_add_tcase(s, tc_alloc);
|
||||
tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);
|
||||
|
|
|
@ -126,11 +126,13 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="chardata.c" />
|
||||
<ClCompile Include="minicheck.c" />
|
||||
<ClCompile Include="memcheck.h" />
|
||||
<ClCompile Include="runtests.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="chardata.h" />
|
||||
<ClInclude Include="minicheck.h" />
|
||||
<ClInclude Include="memcheck.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
<ClCompile Include="minicheck.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="memcheck.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="runtests.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -32,5 +35,8 @@
|
|||
<ClInclude Include="minicheck.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="memcheck.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Add table
Reference in a new issue