# HG changeset patch # User Mike Becker # Date 1747923835 -7200 # Node ID 7be10b57f658284cec3850c95ad4ee9397e8fd89 # Parent 1003c014bf929ddb4ae22699b00f909530ad2f8b fix critical memory overflow in the stack-based array reallocator diff -r 1003c014bf92 -r 7be10b57f658 CHANGELOG --- a/CHANGELOG Thu May 22 16:03:20 2025 +0200 +++ b/CHANGELOG Thu May 22 16:23:55 2025 +0200 @@ -19,6 +19,7 @@ * changes the implementation of cx_strreplacen() for improved efficiency * changes all cxListIterator() without index to also accept NULL as list argument * changes insert_element member function of CxList to accept NULL source and return a pointer to the inserted element + * fixes critical memory overflow in the stack-based array reallocator (this unfortunately breaks the function signature) * fixes that starting an iteration in a non-root node incorrectly continues iteration with the siblings of that node * fixes unnecessary allocations in cx_strcat() family of functions * fixes errno value after failing cxBufferSeek() to be consistently EINVAL diff -r 1003c014bf92 -r 7be10b57f658 docs/Writerside/topics/about.md --- a/docs/Writerside/topics/about.md Thu May 22 16:03:20 2025 +0200 +++ b/docs/Writerside/topics/about.md Thu May 22 16:23:55 2025 +0200 @@ -46,6 +46,7 @@ * changes the implementation of cx_strreplacen() for improved efficiency * changes all cxListIterator() without index to also accept NULL as list argument * changes insert_element member function of CxList to accept NULL source and return a pointer to the inserted element +* fixes critical memory overflow in the stack-based array reallocator (this unfortunately breaks the function signature) * fixes that starting an iteration in a non-root node incorrectly continues iteration with the siblings of that node * fixes unnecessary allocations in cx_strcat() family of functions * fixes errno value after failing cxBufferSeek() to be consistently EINVAL diff -r 1003c014bf92 -r 7be10b57f658 docs/Writerside/topics/array_list.h.md --- a/docs/Writerside/topics/array_list.h.md Thu May 22 16:03:20 2025 +0200 +++ b/docs/Writerside/topics/array_list.h.md Thu May 22 16:23:55 2025 +0200 @@ -93,8 +93,9 @@ #include typedef struct { - void *(*realloc)(void *array, size_t capacity, size_t elem_size, - CxArrayReallocator *alloc); + void *(*realloc)(void *array, + size_t old_capacity, size_t new_capacity, + size_t elem_size, CxArrayReallocator *alloc); void *ptr1; void *ptr2; size_t int1; diff -r 1003c014bf92 -r 7be10b57f658 src/array_list.c --- a/src/array_list.c Thu May 22 16:03:20 2025 +0200 +++ b/src/array_list.c Thu May 22 16:23:55 2025 +0200 @@ -36,12 +36,13 @@ static void *cx_array_default_realloc( void *array, - size_t capacity, + cx_attr_unused size_t old_capacity, + size_t new_capacity, size_t elem_size, cx_attr_unused CxArrayReallocator *alloc ) { size_t n; - if (cx_szmul(capacity, elem_size, &n)) { + if (cx_szmul(new_capacity, elem_size, &n)) { errno = EOVERFLOW; return NULL; } @@ -58,13 +59,14 @@ static void *cx_array_advanced_realloc( void *array, - size_t capacity, + size_t old_capacity, + size_t new_capacity, size_t elem_size, cx_attr_unused CxArrayReallocator *alloc ) { // check for overflow size_t n; - if (cx_szmul(capacity, elem_size, &n)) { + if (cx_szmul(new_capacity, elem_size, &n)) { errno = EOVERFLOW; return NULL; } @@ -77,7 +79,7 @@ if (array == alloc->ptr2) { newmem = cxMalloc(al, n); if (newmem != NULL && array != NULL) { - memcpy(newmem, array, n); + memcpy(newmem, array, old_capacity*elem_size); } } else { newmem = cxRealloc(al, array, n); @@ -180,7 +182,7 @@ // perform reallocation void *newmem = reallocator->realloc( - *array, newcap, elem_size, reallocator + *array, oldcap, newcap, elem_size, reallocator ); if (newmem == NULL) { return 1; // LCOV_EXCL_LINE @@ -286,7 +288,7 @@ // perform reallocation void *newmem = reallocator->realloc( - *target, newcap, elem_size, reallocator + *target, oldcap, newcap, elem_size, reallocator ); if (newmem == NULL) { return 1; @@ -366,13 +368,14 @@ // store some counts size_t old_size = *size; + size_t old_capacity = *capacity; size_t needed_capacity = old_size + elem_count; // if we need more than we have, try a reallocation - if (needed_capacity > *capacity) { + if (needed_capacity > old_capacity) { size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); void *new_mem = reallocator->realloc( - *target, new_capacity, elem_size, reallocator + *target, old_capacity, new_capacity, elem_size, reallocator ); if (new_mem == NULL) { // give it up right away, there is no contract diff -r 1003c014bf92 -r 7be10b57f658 src/cx/array_list.h --- a/src/cx/array_list.h Thu May 22 16:03:20 2025 +0200 +++ b/src/cx/array_list.h Thu May 22 16:23:55 2025 +0200 @@ -178,17 +178,19 @@ * or to transport other additional data. * * @param array the array to reallocate - * @param capacity the new capacity (number of elements) + * @param old_capacity the old number of elements + * @param new_capacity the new number of elements * @param elem_size the size of each element * @param alloc a reference to this allocator * @return a pointer to the reallocated memory or @c NULL on failure */ cx_attr_nodiscard - cx_attr_nonnull_arg(4) - cx_attr_allocsize(2, 3) + cx_attr_nonnull_arg(5) + cx_attr_allocsize(3, 4) void *(*realloc)( void *array, - size_t capacity, + size_t old_capacity, + size_t new_capacity, size_t elem_size, struct cx_array_reallocator_s *alloc );