-
Notifications
You must be signed in to change notification settings - Fork 42
Add cupsArrayDup test and overflow protection in array allocation #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,18 @@ 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 > SIZE_MAX / size) | ||
| return (false); | ||
|
|
||
| *bytes = count * size; | ||
| return (true); | ||
| } | ||
|
|
||
|
|
||
| // | ||
| // 'cupsArrayAdd()' - Add an element to an array. | ||
|
|
@@ -280,8 +292,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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assuming that you are duplicating an array you've created using the normal APIs, it is unlikely that duplicating will overflow. But for completeness-sake we can verify that |
||
|
|
||
| if (!cups_array_multiply(a->num_elements, sizeof(void *), &bytes) || | ||
| (da->elements = malloc(bytes)) == NULL) | ||
| { | ||
| free(da); | ||
| return (NULL); | ||
|
|
@@ -306,6 +320,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 */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copying the hash is important, but again the size should already be limited. |
||
| 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 +684,18 @@ cupsArrayNew(cups_array_cb_t f, // I - Comparison callback function or `NULL` f | |
|
|
||
| if (hsize > 0 && hf) | ||
| { | ||
| size_t bytes; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a #define _CUPS_MAXHASH 0x1fffffff
...
if (hsize > 0 && hsize <= _CUPS_MAXHASH && hf)
{
...
} |
||
|
|
||
| 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 +895,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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Limit count to |
||
|
|
||
| if (!cups_array_multiply(count, sizeof(void *), &bytes) || | ||
| (temp = realloc(a->elements, bytes)) == NULL) | ||
| return (false); | ||
|
|
||
| a->alloc_elements = count; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| Test helper: cupsArrayDup | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NO. The "testarray.c" unit test program tests the |
||
|
|
||
| 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. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| #include <stdio.h> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be part of the existing "testarray.c". |
||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #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; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, let's just define a reasonable maximum for
num_elements(maybe 2^30-1 which is ~1 billion elements) and limit it accordingly.