--- a/src/array_list.c Fri Dec 20 21:25:33 2024 +0100 +++ b/src/array_list.c Sat Dec 21 21:03:28 2024 +0100 @@ -40,7 +40,12 @@ size_t elem_size, cx_attr_unused CxArrayReallocator *alloc ) { - return realloc(array, capacity * elem_size); + size_t n; + if (cx_szmul(capacity, elem_size, &n)) { + errno = EOVERFLOW; + return NULL; + } + return realloc(array, n); } CxArrayReallocator cx_array_default_reallocator_impl = { @@ -57,18 +62,25 @@ size_t elem_size, cx_attr_unused CxArrayReallocator *alloc ) { + // check for overflow + size_t n; + if (cx_szmul(capacity, elem_size, &n)) { + errno = EOVERFLOW; + return NULL; + } + // retrieve the pointer to the actual allocator const CxAllocator *al = alloc->ptr1; // check if the array is still located on the stack void *newmem; if (array == alloc->ptr2) { - newmem = cxMalloc(al, capacity * elem_size); + newmem = cxMalloc(al, n); if (newmem != NULL && array != NULL) { - memcpy(newmem, array, capacity * elem_size); + memcpy(newmem, array, n); } } else { - newmem = cxRealloc(al, array, capacity * elem_size); + newmem = cxRealloc(al, array, n); } return newmem; } @@ -89,6 +101,18 @@ // LOW LEVEL ARRAY LIST FUNCTIONS +static size_t cx_array_align_capacity( + size_t cap, + size_t alignment, + size_t max +) { + if (cap > max - alignment) { + return cap; + } else { + return cap - (cap % alignment) + alignment; + } +} + int cx_array_reserve( void **array, void *size, @@ -136,19 +160,19 @@ // assert that the array is allocated when it has capacity assert(*array != NULL || oldcap == 0); - // determine new capacity - size_t newcap = oldsize + elem_count; - // check for overflow - if (newcap > max_size) { + if (elem_count > max_size - oldsize) { errno = EOVERFLOW; return 1; } + // determine new capacity + size_t newcap = oldsize + elem_count; + // reallocate if possible if (newcap > oldcap) { // calculate new capacity (next number divisible by 16) - newcap = newcap - (newcap % 16) + 16; + newcap = cx_array_align_capacity(newcap, 16, max_size); // perform reallocation void *newmem = reallocator->realloc( @@ -229,16 +253,16 @@ // assert that the array is allocated when it has capacity assert(*target != NULL || oldcap == 0); + // check for overflow + if (index > max_size || elem_count > max_size - index) { + errno = EOVERFLOW; + return 1; + } + // check if resize is required size_t minsize = index + elem_count; size_t newsize = oldsize < minsize ? minsize : oldsize; - // check for overflow - if (newsize > max_size) { - errno = EOVERFLOW; - return 1; - } - // reallocate if possible size_t newcap = oldcap; if (newsize > oldcap) { @@ -249,7 +273,7 @@ && srcaddr < targetaddr + oldcap * elem_size; // calculate new capacity (next number divisible by 16) - newcap = newsize - (newsize % 16) + 16; + newcap = cx_array_align_capacity(newsize, 16, max_size); assert(newcap > newsize); // perform reallocation @@ -274,6 +298,7 @@ start += index * elem_size; // copy elements and set new size + // note: no overflow check here, b/c we cannot get here w/o allocation memmove(start, src, elem_count * elem_size); // if any of size or capacity changed, store them back @@ -321,13 +346,19 @@ // corner case if (elem_count == 0) return 0; + // overflow check + if (elem_count > SIZE_MAX - *size) { + errno = EOVERFLOW; + return 1; + } + // store some counts size_t old_size = *size; size_t needed_capacity = old_size + elem_count; // if we need more than we have, try a reallocation if (needed_capacity > *capacity) { - size_t new_capacity = needed_capacity - (needed_capacity % 16) + 16; + size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); void *new_mem = reallocator->realloc( *target, new_capacity, elem_size, reallocator );