src/array_list.c

changeset 1040
1ecf4dbbc60c
parent 1022
2911c1f4a570
--- 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
         );

mercurial