]> granicus.if.org Git - libexpat/commitdiff
Test to catch Issue #17
authorRhodri James <rhodri@kynesim.co.uk>
Tue, 25 Apr 2017 22:01:56 +0000 (00:01 +0200)
committerSebastian Pipping <sebastian@pipping.org>
Tue, 25 Apr 2017 22:04:52 +0000 (00:04 +0200)
expat/CMakeLists.txt
expat/Makefile.in
expat/tests/memcheck.c [new file with mode: 0644]
expat/tests/memcheck.h [new file with mode: 0644]
expat/tests/runtests.c
expat/tests/runtests.vcxproj
expat/tests/runtests.vcxproj.filters

index 103d31c7b6f23f688b50d35b1e34dab0ae0c0c3f..d3bb7b80a1d3873049fbd15efb2a49ecca641a98 100755 (executable)
@@ -142,12 +142,12 @@ endif(BUILD_examples)
 \r
 if(BUILD_tests)\r
     ## these are unittests that can be run on any platform\r
-    add_executable(runtests tests/runtests.c tests/chardata.c tests/minicheck.c)\r
+    add_executable(runtests tests/runtests.c tests/chardata.c tests/minicheck.c tests/memcheck.c)\r
     set_property(TARGET runtests PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)\r
     target_link_libraries(runtests expat)\r
     add_test(runtests tests/runtests)\r
 \r
-    add_executable(runtestspp tests/runtestspp.cpp tests/chardata.c tests/minicheck.c)\r
+    add_executable(runtestspp tests/runtestspp.cpp tests/chardata.c tests/minicheck.c tests/memcheck.c)\r
     set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)\r
     target_link_libraries(runtestspp expat)\r
     add_test(runtestspp tests/runtestspp)\r
index 07c7f46e1216b4dad220f6f5e5a60a6c8dcdd04e..3fcbca34f1ff500735bcadf542200cb0cf20c644 100644 (file)
@@ -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)
diff --git a/expat/tests/memcheck.c b/expat/tests/memcheck.c
new file mode 100644 (file)
index 0000000..0b1e533
--- /dev/null
@@ -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;
+}
diff --git a/expat/tests/memcheck.h b/expat/tests/memcheck.h
new file mode 100644 (file)
index 0000000..2c92724
--- /dev/null
@@ -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
index 84b5577fe73cedaeeb646fe9aa3917d3bfbdfb70..8e74c34e72c176bcd716bd7ee9f82bed81fb59f9 100644 (file)
@@ -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);
index fc30805b5f3e999f3e3327548f33352a07292ca6..3ac7f3aa9294cfff0c9d55db561ccd151cf9eacf 100644 (file)
   <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">
index 95ebe9cd6b3e6e6b17be7e0d110c570ed8285543..ca66cb56ffcb713ea96391e66b0701fb7bfc7df0 100644 (file)
@@ -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>
\ No newline at end of file