diff --git a/cups/array.c b/cups/array.c index ab627d66c..c820f721d 100644 --- a/cups/array.c +++ b/cups/array.c @@ -19,6 +19,7 @@ // #define _CUPS_MAXSAVE 32 // Maximum number of saves +#define _CUPS_ARRAY_MAX_ELEM 0x1fffffff // Maximum number of elements // @@ -58,6 +59,21 @@ struct _cups_array_s // CUPS array structure static bool cups_array_add(cups_array_t *a, void *e, bool insert); static size_t cups_array_find(cups_array_t *a, void *e, size_t prev, int *rdiff); +static bool cups_array_multiply(size_t count, size_t size, size_t *bytes); + + +static bool cups_array_multiply(size_t count, size_t size, size_t *bytes) +{ + if (count > _CUPS_ARRAY_MAX_ELEM || size > SIZE_MAX / _CUPS_ARRAY_MAX_ELEM) + return (false); + + if (count > (SIZE_MAX / size)) + return (false); + + *bytes = count * size; + return (true); +} + // // 'cupsArrayAdd()' - Add an element to an array. @@ -280,8 +296,10 @@ cupsArrayDup(cups_array_t *a) // I - Array if (a->num_elements) { // Allocate memory for the elements... - da->elements = malloc((size_t)a->num_elements * sizeof(void *)); - if (!da->elements) + size_t bytes; + + if (!cups_array_multiply(a->num_elements, sizeof(void *), &bytes) || + (da->elements = malloc(bytes)) == NULL) { free(da); return (NULL); @@ -306,6 +324,23 @@ cupsArrayDup(cups_array_t *a) // I - Array da->alloc_elements = a->num_elements; } + /* Copy hash table if present so duplicated arrays preserve hashed lookups */ + if (a->hash) + { + size_t bytes; + + da->hashsize = a->hashsize; + if (!cups_array_multiply(da->hashsize, sizeof(size_t), &bytes) || + (da->hash = malloc(bytes)) == NULL) + { + if (da->elements) + free(da->elements); + free(da); + return (NULL); + } + memcpy(da->hash, a->hash, bytes); + } + // Return the new array... return (da); } @@ -653,17 +688,18 @@ cupsArrayNew(cups_array_cb_t f, // I - Comparison callback function or `NULL` f if (hsize > 0 && hf) { + size_t bytes; + a->hashfunc = hf; a->hashsize = hsize; - a->hash = malloc((size_t)hsize * sizeof(size_t)); - - if (!a->hash) + if (!cups_array_multiply(hsize, sizeof(size_t), &bytes) || + (a->hash = malloc(bytes)) == NULL) { free(a); return (NULL); } - memset(a->hash, -1, (size_t)hsize * sizeof(size_t)); + memset(a->hash, -1, bytes); } a->copyfunc = cf; @@ -863,7 +899,10 @@ cups_array_add(cups_array_t *a, // I - Array else count = a->alloc_elements + 1024; - if ((temp = realloc(a->elements, count * sizeof(void *))) == NULL) + size_t bytes; + + if (!cups_array_multiply(count, sizeof(void *), &bytes) || + (temp = realloc(a->elements, bytes)) == NULL) return (false); a->alloc_elements = count; diff --git a/cups/testarray.c b/cups/testarray.c index 5755e9492..bcca62856 100644 --- a/cups/testarray.c +++ b/cups/testarray.c @@ -15,6 +15,8 @@ #include "dir.h" #include "test-internal.h" +#define _CUPS_ARRAY_MAX_ELEM 0x1fffffff + // // Local functions... @@ -207,6 +209,35 @@ main(void) status ++; } + // cupsArrayDup() limit check + testBegin("cupsArrayDup limit"); + { + cups_array_t *limit_array = calloc(1, sizeof(cups_array_t)); + + if (!limit_array) + { + testEndMessage(false, "unable to allocate test array"); + status ++; + } + else + { + limit_array->num_elements = _CUPS_ARRAY_MAX_ELEM + 1; + limit_array->elements = NULL; + + if (cupsArrayDup(limit_array) != NULL) + { + testEndMessage(false, "accepted an oversized element count"); + status ++; + } + else + { + testEnd(true); + } + + free(limit_array); + } + } + // cupsArrayRemove() testBegin("cupsArrayRemove"); if (cupsArrayRemove(array, (void *)"One Fish") && cupsArrayGetCount(array) == 3) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..592eb072c --- /dev/null +++ b/tests/README.md @@ -0,0 +1,19 @@ +Test helper: cupsArrayDup + +Build and run (POSIX shell or MSYS2/WSL): + +```bash +cd path/to/libcups +# build the project first so headers and deps are available +./configure +make -j + +# then compile the test (from the project root) +gcc -I. tests/test_array_dup.c cups/array.c -o tests/test_array_dup + +# run +./tests/test_array_dup +``` + +On Windows with MSVC, compile the project with Visual Studio and link the test +against the built library, or run the test from within the project build. diff --git a/tests/test_array_dup.c b/tests/test_array_dup.c new file mode 100644 index 000000000..2610d98e0 --- /dev/null +++ b/tests/test_array_dup.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include "cups/array.h" + +// Simple hash: sum of bytes modulo size +static size_t +simple_hash(void *element, void *data) +{ + const char *s = (const char *)element; + size_t sum = 0; + + (void)data; + + for (; *s; s++) + sum += (unsigned char)*s; + + return (sum % ((size_t)data)); +} + +int main(void) +{ + const char *values[] = { "apple", "banana", "cherry", "date", "elderberry", NULL }; + cups_array_t *a, *d; + size_t i; + + // Create an array with hashing enabled (hash size 16). Use strdup/free for copy/free. + a = cupsArrayNew((cups_array_cb_t)strcmp, NULL, (cups_ahash_cb_t)simple_hash, 16, (cups_acopy_cb_t)strdup, (cups_afree_cb_t)free); + if (!a) + { + fprintf(stderr, "Failed to create array\n"); + return 2; + } + + for (i = 0; values[i]; i++) + { + if (!cupsArrayAdd(a, (void *)values[i])) + { + fprintf(stderr, "Failed to add %s\n", values[i]); + cupsArrayDelete(a); + return 3; + } + } + + // Duplicate the array + d = cupsArrayDup(a); + if (!d) + { + fprintf(stderr, "cupsArrayDup() failed\n"); + cupsArrayDelete(a); + return 4; + } + + // Verify all values can be found in the duplicate + for (i = 0; values[i]; i++) + { + void *found = cupsArrayFind(d, (void *)values[i]); + if (!found) + { + fprintf(stderr, "Value '%s' not found in duplicated array\n", values[i]); + cupsArrayDelete(a); + cupsArrayDelete(d); + return 5; + } + else + printf("Found '%s' in duplicate\n", (char *)found); + } + + cupsArrayDelete(a); + cupsArrayDelete(d); + + // Regression test for integer overflow protection in array allocation. + { + cups_array_t *bad = calloc(1, sizeof(cups_array_t)); + size_t bad_count = SIZE_MAX / sizeof(void *) + 1; + + if (!bad) + { + fprintf(stderr, "Failed to allocate array skeleton for overflow test\n"); + return 6; + } + + bad->num_elements = bad_count; + bad->elements = NULL; + bad->copyfunc = NULL; + bad->freefunc = NULL; + + if (cupsArrayDup(bad) != NULL) + { + fprintf(stderr, "cupsArrayDup() did not reject overflow count\n"); + free(bad); + return 7; + } + + free(bad); + } + + printf("Test passed\n"); + return 0; +}