Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions cups/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Copy link
Copy Markdown
Member

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.

#define _CUPS_ARRAY_MAX_ELEM 0x1fffffff



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.
Expand Down Expand Up @@ -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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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 a->num_elements <= _CUPS_MAXELEM before trying to allocate that much memory.


if (!cups_array_multiply(a->num_elements, sizeof(void *), &bytes) ||
(da->elements = malloc(bytes)) == NULL)
{
free(da);
return (NULL);
Expand All @@ -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 */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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);
}
Expand Down Expand Up @@ -653,17 +684,18 @@ cupsArrayNew(cups_array_cb_t f, // I - Comparison callback function or `NULL` f

if (hsize > 0 && hf)
{
size_t bytes;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a _CUPS_MAXHASH limit and ignore the hashing if it exceeds the limit, e.g.:

#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;
Expand Down Expand Up @@ -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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limit count to _CUPS_MAXELEMS.


if (!cups_array_multiply(count, sizeof(void *), &bytes) ||
(temp = realloc(a->elements, bytes)) == NULL)
return (false);

a->alloc_elements = count;
Expand Down
19 changes: 19 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Test helper: cupsArrayDup

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NO.

The "testarray.c" unit test program tests the cupsArray API. Add to that please.


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.
100 changes: 100 additions & 0 deletions tests/test_array_dup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <stdio.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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;
}