[core] refactor array search; raise array size limit to SSIZE_MAX

- raise array size limit from INT_MAX to SSIZE_MAX. INT_MAX already
  is way to high to be hit in any sane scenario, but SSIZE_MAX can
  *obviously* not be hit due to memory constraints.
- use size_t for array indices instead of int
- use binary search instead of next_power_of_2 hack; document invariants
  and check them in debug mode (asserts).
- return the actual insert position instead of something near

From: Stefan Bühler <stbuehler@web.de>

git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@3100 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/heads/lighttpd-1.4.x
Stefan Bühler 7 years ago
parent be121a638b
commit 224bf545c1

@ -28,6 +28,7 @@ NEWS
* [mod_secdownload] fix buffer overflow in secdl_verify_mac (reported by Fortify Open Review Project)
* [mod_fastcgi,mod_scgi] fix leaking file-descriptor when backend spawning failed (reported by Fortify Open Review Project)
* [core] improve array API to prevent memory leaks
* [core] refactor array search; raise array size limit to SSIZE_MAX
- 1.4.39 - 2016-01-02
* [core] fix memset_s call (fixes #2698)

@ -9,14 +9,14 @@
#include <errno.h>
#include <assert.h>
#define ARRAY_NOT_FOUND ((size_t)(-1))
array *array_init(void) {
array *a;
a = calloc(1, sizeof(*a));
force_assert(a);
a->next_power_of_2 = 1;
return a;
}
@ -28,7 +28,6 @@ array *array_init_array(array *src) {
a->used = src->used;
a->size = src->size;
a->next_power_of_2 = src->next_power_of_2;
a->unique_ndx = src->unique_ndx;
a->data = malloc(sizeof(*src->data) * src->size);
@ -87,47 +86,48 @@ data_unset *array_pop(array *a) {
return du;
}
static int array_get_index(array *a, const char *key, size_t keylen, int *rndx) {
int ndx = -1;
int i, pos = 0;
if (key == NULL) return -1;
/* try to find the string */
for (i = pos = a->next_power_of_2 / 2; ; i >>= 1) {
int cmp;
if (pos < 0) {
pos += i;
} else if (pos >= (int)a->used) {
pos -= i;
/* returns index of element or ARRAY_NOT_FOUND
* if rndx != NULL it stores the position in a->sorted[] where the key needs
* to be inserted
*/
static size_t array_get_index(array *a, const char *key, size_t keylen, size_t *rndx) {
/* invariant: [lower-1] < key < [upper]
* "virtual elements": [-1] = -INFTY, [a->used] = +INFTY
* also an invariant: 0 <= lower <= upper <= a->used
*/
size_t lower = 0, upper = a->used;
force_assert(upper <= SSIZE_MAX); /* (lower + upper) can't overflow */
while (lower != upper) {
size_t probe = (lower + upper) / 2;
int cmp = buffer_caseless_compare(key, keylen, CONST_BUF_LEN(a->data[a->sorted[probe]]->key));
assert(lower < upper); /* from loop invariant (lower <= upper) + (lower != upper) */
assert((lower <= probe) && (probe < upper)); /* follows from lower < upper */
if (cmp == 0) {
/* found */
if (rndx) *rndx = probe;
return a->sorted[probe];
} else if (cmp < 0) {
/* key < [probe] */
upper = probe; /* still: lower <= upper */
} else {
cmp = buffer_caseless_compare(key, keylen, CONST_BUF_LEN(a->data[a->sorted[pos]]->key));
if (cmp == 0) {
/* found */
ndx = a->sorted[pos];
break;
} else if (cmp < 0) {
pos -= i;
} else {
pos += i;
}
/* key > [probe] */
lower = probe + 1; /* still: lower <= upper */
}
if (i == 0) break;
}
if (rndx) *rndx = pos;
return ndx;
/* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */
if (rndx) *rndx = lower;
return ARRAY_NOT_FOUND;
}
data_unset *array_get_element(array *a, const char *key) {
int ndx;
if (-1 != (ndx = array_get_index(a, key, strlen(key), NULL))) {
/* found, leave here */
size_t ndx;
force_assert(NULL != key);
if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, key, strlen(key), NULL))) {
/* found, return it */
return a->data[ndx];
}
@ -172,9 +172,7 @@ void array_set_key_value(array *hdrs, const char *key, size_t key_len, const cha
/* if entry already exists return pointer to existing entry, otherwise insert entry and return NULL */
static data_unset **array_find_or_insert(array *a, data_unset *entry) {
int ndx = -1;
int pos = 0;
size_t j;
size_t ndx, pos, j;
/* generate unique index if neccesary */
if (buffer_is_empty(entry->key) || entry->is_index_key) {
@ -184,17 +182,15 @@ static data_unset **array_find_or_insert(array *a, data_unset *entry) {
}
/* try to find the entry */
if (-1 != (ndx = array_get_index(a, CONST_BUF_LEN(entry->key), &pos))) {
if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, CONST_BUF_LEN(entry->key), &pos))) {
/* found collision, return it */
return &a->data[ndx];
}
/* insert */
/* there is no good error handling for hitting this limit
* (we can't handle more then INT_MAX entries: see array_get_index())
*/
force_assert(a->used + 1 <= INT_MAX);
/* there couldn't possibly be enough memory to store so many entries */
force_assert(a->used + 1 <= SSIZE_MAX);
if (a->size == 0) {
a->size = 16;
@ -212,19 +208,13 @@ static data_unset **array_find_or_insert(array *a, data_unset *entry) {
for (j = a->used; j < a->size; j++) a->data[j] = NULL;
}
ndx = (int) a->used;
ndx = a->used;
/* make sure there is nothing here */
if (a->data[ndx]) a->data[ndx]->free(a->data[ndx]);
a->data[a->used++] = entry;
if (pos != ndx &&
((pos < 0) ||
buffer_caseless_compare(CONST_BUF_LEN(entry->key), CONST_BUF_LEN(a->data[a->sorted[pos]]->key)) > 0)) {
pos++;
}
/* move everything one step to the right */
if (pos != ndx) {
memmove(a->sorted + (pos + 1), a->sorted + (pos), (ndx - pos) * sizeof(*a->sorted));
@ -233,8 +223,6 @@ static data_unset **array_find_or_insert(array *a, data_unset *entry) {
/* insert */
a->sorted[pos] = ndx;
if (a->next_power_of_2 == (size_t)ndx) a->next_power_of_2 <<= 1;
return NULL;
}

@ -35,12 +35,11 @@ typedef struct {
size_t *sorted;
size_t used;
size_t used; /* <= SSIZE_MAX */
size_t size;
size_t unique_ndx;
size_t next_power_of_2;
int is_weakref; /* data is weakref, don't bother the data */
} array;

Loading…
Cancel
Save