mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 02:08:22 -10:00
libretro-common: harden linked_list allocation, drop string_list growth memset
linked_list: linked_list_add(), linked_list_insert() and linked_list_iterator() dereferenced malloc() results without a NULL check. Bail out on OOM instead (list unchanged / NULL iterator), matching the treatment generic_queue already received. string_list: string_list_capacity() zeroed the entire newly grown half of the element array on every doubling. Slots in [size, cap) are never read - string_list_free() walks [0, size) and the append paths fully initialise a slot when it becomes live - so the memset was pure bandwidth (hundreds of thousands of elements cleared to add one entry on large lists). Remove it and have string_list_append()/_append_n() set userdata = NULL explicitly so string_list_free() cannot free garbage. string_list_clone() already calloc()s its own array and every external writer of elems[].userdata targets size - 1, so nothing relied on the pre-zeroing. Also guard the cap * sizeof(elem) multiplication in string_list_capacity() and the cap * 2 doubling in the append paths against size_t overflow.
This commit is contained in:
1 parent
963b5f06a3
commit
914f0e26f1
2 files changed
+48
-18
No files matched your search
@@ -84,7 +84,10 @@ void linked_list_add(linked_list_t *list, void *value)
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
new_item = (struct linked_list_item_t *)malloc(sizeof(struct linked_list_item_t));
|
||||
/* NULL-check malloc: the field writes below would dereference NULL.
|
||||
* The API returns void, so an OOM leaves the list unchanged. */
|
||||
if (!(new_item = (struct linked_list_item_t *)malloc(sizeof(struct linked_list_item_t))))
|
||||
return;
|
||||
new_item->value = value;
|
||||
new_item->previous = list->last_item;
|
||||
new_item->next = NULL;
|
||||
@@ -116,7 +119,9 @@ void linked_list_insert(linked_list_t *list, size_t index, void *value)
|
||||
next_item = next_item->next;
|
||||
}
|
||||
|
||||
new_item = (struct linked_list_item_t *)malloc(sizeof(struct linked_list_item_t));
|
||||
/* NULL-check malloc: see linked_list_add. */
|
||||
if (!(new_item = (struct linked_list_item_t *)malloc(sizeof(struct linked_list_item_t))))
|
||||
return;
|
||||
new_item->value = value;
|
||||
|
||||
if (previous_item)
|
||||
@@ -403,7 +408,10 @@ linked_list_iterator_t *linked_list_iterator(linked_list_t *list, bool forward)
|
||||
if (!list || !list->first_item)
|
||||
return NULL;
|
||||
|
||||
iterator = (linked_list_iterator_t *)malloc(sizeof(linked_list_iterator_t));
|
||||
/* NULL-check malloc: callers already handle a NULL iterator
|
||||
* (returned for an empty list). */
|
||||
if (!(iterator = (linked_list_iterator_t *)malloc(sizeof(linked_list_iterator_t))))
|
||||
return NULL;
|
||||
iterator->list = list;
|
||||
iterator->item = forward ? list->first_item : list->last_item;
|
||||
iterator->forward = forward;
|
||||
|
||||
@@ -54,16 +54,24 @@ static bool string_list_deinitialize_internal(struct string_list *list)
|
||||
|
||||
bool string_list_capacity(struct string_list *list, size_t cap)
|
||||
{
|
||||
struct string_list_elem *new_data = (struct string_list_elem*)
|
||||
struct string_list_elem *new_data;
|
||||
|
||||
/* Guard the byte-count multiplication: a huge cap would wrap and
|
||||
* realloc a buffer far smaller than the caller expects. */
|
||||
if (cap > SIZE_MAX / sizeof(*new_data))
|
||||
return false;
|
||||
|
||||
new_data = (struct string_list_elem*)
|
||||
realloc(list->elems, cap * sizeof(*new_data));
|
||||
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
if (cap > list->cap)
|
||||
memset(&new_data[list->cap], 0,
|
||||
sizeof(*new_data) * (cap - list->cap));
|
||||
|
||||
/* Slots in [size, cap) are never read: string_list_free() only walks
|
||||
* [0, size), and the append paths fully initialise a slot when it
|
||||
* becomes live. So there is no need to zero the whole new half on
|
||||
* every doubling, which for large lists was a memset over hundreds of
|
||||
* thousands of elements to add one entry. */
|
||||
list->elems = new_data;
|
||||
list->cap = cap;
|
||||
return true;
|
||||
@@ -142,17 +150,24 @@ bool string_list_append(struct string_list *list, const char *elem,
|
||||
{
|
||||
char *data_dup = NULL;
|
||||
|
||||
if ( list->size >= list->cap
|
||||
&& !string_list_capacity(list,
|
||||
if (list->size >= list->cap)
|
||||
{
|
||||
if (list->cap > SIZE_MAX / 2)
|
||||
return false;
|
||||
if (!string_list_capacity(list,
|
||||
(list->cap > 0) ? (list->cap * 2) : 32))
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
data_dup = strdup(elem);
|
||||
if (!data_dup)
|
||||
return false;
|
||||
|
||||
list->elems[list->size].data = data_dup;
|
||||
list->elems[list->size].attr = attr;
|
||||
list->elems[list->size].data = data_dup;
|
||||
list->elems[list->size].attr = attr;
|
||||
/* Slot is not pre-zeroed (see string_list_capacity); userdata must
|
||||
* be NULL so string_list_free() does not free garbage. */
|
||||
list->elems[list->size].userdata = NULL;
|
||||
list->size++;
|
||||
|
||||
return true;
|
||||
@@ -163,10 +178,14 @@ bool string_list_append_n(struct string_list *list, const char *elem,
|
||||
{
|
||||
char *data_dup = NULL;
|
||||
|
||||
if ( list->size >= list->cap
|
||||
&& !string_list_capacity(list,
|
||||
if (list->size >= list->cap)
|
||||
{
|
||||
if (list->cap > SIZE_MAX / 2)
|
||||
return false;
|
||||
if (!string_list_capacity(list,
|
||||
(list->cap > 0) ? (list->cap * 2) : 32))
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
data_dup = (char*)malloc(len + 1);
|
||||
if (!data_dup)
|
||||
@@ -174,8 +193,11 @@ bool string_list_append_n(struct string_list *list, const char *elem,
|
||||
memcpy(data_dup, elem, len);
|
||||
data_dup[len] = '\0';
|
||||
|
||||
list->elems[list->size].data = data_dup;
|
||||
list->elems[list->size].attr = attr;
|
||||
list->elems[list->size].data = data_dup;
|
||||
list->elems[list->size].attr = attr;
|
||||
/* Slot is not pre-zeroed (see string_list_capacity); userdata must
|
||||
* be NULL so string_list_free() does not free garbage. */
|
||||
list->elems[list->size].userdata = NULL;
|
||||
list->size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user