Commit 5874334
committed
runahead: fix OOM NULL-derefs in input-state tracking and savestate allocation
Five bugs in two call paths (input-state list management for
runahead's per-frame input replay, and the savestate ring
buffer).
=== runahead.c: input_list_element_constructor ===
void *ptr = malloc(sizeof(input_list_element));
input_list_element *element = (input_list_element*)ptr;
element->port = 0; /* NULL-deref on OOM */
...
element->state = (int16_t*)calloc(NAME_MAX_LENGTH,
sizeof(int16_t));
element->state_size = NAME_MAX_LENGTH;
return ptr;
Two stacked unchecked allocs. The outer malloc failure would
NULL-deref in the field writes immediately below. The inner
calloc failure is subtler: ptr itself is still valid and would
be returned, but element->state = NULL while element->state_size
is set to a non-zero value (NAME_MAX_LENGTH). The caller
(runahead_input_state_set_last) then does
'element->state[id] = value' on what looks like a valid element
and NULL-derefs.
Fix: NULL-check the outer malloc (return NULL for the caller,
which already handles it via 'if (element)'). NULL-check the
inner calloc, free the outer allocation, and return NULL
similarly. Also moved the element->state_size = NAME_MAX_LENGTH
assignment to after the successful calloc so state/state_size
stay consistent.
=== runahead.c: input_list_element_realloc ===
if (new_size > element->state_size)
{
element->state = (int16_t*)realloc(element->state,
new_size * sizeof(int16_t));
memset(&element->state[element->state_size], 0,
(new_size - element->state_size) * sizeof(int16_t));
element->state_size = new_size;
}
Classic realloc-assign-self with no NULL check: on OOM
element->state becomes NULL, then '&element->state[element->
state_size]' performs pointer arithmetic on NULL (undefined
behaviour) and the memset traps on a garbage address. This is
hit by runahead's per-frame input-state tracking any time a
port/device/index combination not previously seen encounters an
id beyond the element's current state buffer.
Fix: realloc-to-tmp, NULL-check the tmp, only commit the new
pointer and only update state_size on success. Changed return
type from void to bool so callers can see the failure.
=== runahead.c: input_list_element_expand + callers ===
Propagate the realloc failure up through expand() (also now
returns bool) and gate the subsequent element->state[id] = value
writes in runahead_input_state_set_last on expand success. Two
call-sites in that function (the lookup-hit branch and the
append-new-element branch) both now check:
if (id >= element->state_size
&& !input_list_element_expand(element, id))
return;
element->state[id] = value;
Without the gate the pre-patch OOM path would write to state[id]
with state_size still too small, corrupting whatever followed the
state buffer in the heap.
=== runahead.c: runahead_save_state_alloc ===
if (runloop_st->runahead_save_state_size > 0 && ...)
{
savestate->data = malloc(runloop_st->runahead_save_state_size);
savestate->data_const = savestate->data;
savestate->size = runloop_st->runahead_save_state_size;
}
malloc unchecked and size is set regardless of alloc outcome.
On OOM: savestate->data is NULL but savestate->size equals the
full requested size. Downstream readers treat that as a valid
buffer of that size and dereference data.
Fix: only set savestate->size on successful alloc. Keeps the
NULL-data/zero-size invariant that the function's own pre-loop
initialisation (data=NULL, data_const=NULL, size=0) already
establishes for the 'not runahead' case. runahead_save_state_free
correctly handles free(NULL) on the data pointer so the empty
placeholder teardown is safe.
=== Thread-safety ===
All four functions run on the runloop/main thread (runahead
state is single-threaded with respect to the input/runloop
subsystem). No lock discipline changes.
=== Reachability ===
* input_list_element_{constructor,realloc,expand}: hit every
time a core polls a new input id for a port/device/index
combination during runahead frames. For cores with many
input bindings (arcade, racing wheels with button banks,
multi-tap console setups), the id space grows quickly.
OOM at the initial NAME_MAX_LENGTH (typically 4096) calloc
is unlikely on desktops but realistic on memory-tight
embedded targets running runahead with a memory-hungry core.
* runahead_save_state_alloc: runs once per runahead frame
slot at init, plus on every core-requested savestate size
change. runahead_save_state_size can be multi-MB on
modern emulator cores, making the alloc a realistic OOM
site on 128-512MB handhelds.
=== Scope ===
Five bugs in two adjacent-ish functions, all OOM-related, all
in runahead.c. Left the preempt_allocate at line ~1380 alone -
that function does NULL-check its per-frame malloc and
preempt_deinit's 'for i < preempt->frames; free(buffer[i])'
loop correctly handles the partial-init case because preempt
itself is calloc'd (unallocated buffer[i] slots are NULL and
free(NULL) is safe).1 parent 6cf158d commit 5874334
1 file changed
Lines changed: 56 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
717 | 717 | | |
718 | 718 | | |
719 | 719 | | |
720 | | - | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
721 | 730 | | |
| 731 | + | |
722 | 732 | | |
723 | 733 | | |
724 | 734 | | |
725 | 735 | | |
726 | 736 | | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
727 | 747 | | |
728 | 748 | | |
729 | 749 | | |
730 | 750 | | |
731 | 751 | | |
732 | | - | |
| 752 | + | |
733 | 753 | | |
734 | 754 | | |
735 | 755 | | |
736 | 756 | | |
737 | | - | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
738 | 763 | | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
739 | 767 | | |
740 | 768 | | |
741 | 769 | | |
742 | 770 | | |
| 771 | + | |
743 | 772 | | |
744 | 773 | | |
745 | | - | |
| 774 | + | |
746 | 775 | | |
747 | 776 | | |
748 | 777 | | |
749 | 778 | | |
750 | 779 | | |
751 | 780 | | |
752 | 781 | | |
753 | | - | |
| 782 | + | |
754 | 783 | | |
755 | 784 | | |
756 | 785 | | |
| |||
785 | 814 | | |
786 | 815 | | |
787 | 816 | | |
788 | | - | |
789 | | - | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
790 | 823 | | |
791 | 824 | | |
792 | 825 | | |
| |||
801 | 834 | | |
802 | 835 | | |
803 | 836 | | |
804 | | - | |
805 | | - | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
806 | 841 | | |
807 | 842 | | |
808 | 843 | | |
| |||
917 | 952 | | |
918 | 953 | | |
919 | 954 | | |
920 | | - | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
921 | 967 | | |
922 | 968 | | |
923 | 969 | | |
| |||
0 commit comments