Commit 3a5775d
committed
cheat_manager: fix three OOM bug clusters in cheats realloc and memory buffer init
Three separate bug clusters in cheat_manager.c, all OOM paths,
all on hot paths (cheat loading, cheat list grow, memory-map
init at core start).
=== cheat_manager_realloc: realloc-assign-self + leak ===
val = (struct item_cheat*)
realloc(cheat_st->cheats,
new_size * sizeof(struct item_cheat));
cheat_st->cheats = val ? val : NULL;
This looks like a safe realloc-to-tmp but isn't. On OOM 'val'
is NULL and 'cheats' is still the valid old buffer per the C
realloc contract - but the line above explicitly sets cheats
to NULL, dropping the only reference and leaking the entire
container. Worse, each entry in that container has live
.code and .desc strdup'd strings that were about to be freed
by the shrink branch above (lines 561-569 handled indices
[new_size, orig_size) but [0, min(new_size, orig_size))
still held live strings at the moment of the realloc call).
Fix: free the old buffer AND its per-entry .code/.desc strings
explicitly on OOM before NULL'ing cheats. Preserves the
existing failure contract (size=0, cheats=NULL) that callers
at lines ~515 and ~1206 rely on:
/* caller at 515: gates iteration on cheats non-NULL */
for (i = orig_size; cheat_st->cheats && i < cheats; i++)
/* caller at 1206: returns false if realloc returned false */
if (!cheat_manager_realloc(new_size, CHEAT_HANDLER_TYPE_RETRO))
return false;
=== cheat_manager_initialize_memory descriptor loop: two stacked bugs ===
cheat_st->num_memory_buffers++;
if (!cheat_st->memory_buf_list)
cheat_st->memory_buf_list = calloc(1, sizeof(uint8_t *));
else
cheat_st->memory_buf_list = realloc(
cheat_st->memory_buf_list,
sizeof(uint8_t *) * cheat_st->num_memory_buffers);
if (!cheat_st->memory_size_list)
cheat_st->memory_size_list = calloc(1, sizeof(unsigned));
else
{
unsigned *val = realloc(...);
if (val)
cheat_st->memory_size_list = val;
}
cheat_st->memory_buf_list [num - 1] = ...;
cheat_st->memory_size_list[num - 1] = ...;
Two bugs:
1. memory_buf_list uses realloc-assign-self: on OOM the old
buffer leaks and the [num - 1] write below NULL-derefs.
2. memory_size_list uses realloc-into-tmp with a NULL-check
but silently ignores the failure. That leaves the array
one element too short while num_memory_buffers claims the
new size. The [num - 1] write below is then a
heap-buffer-overflow past the end of memory_size_list.
Plus num_memory_buffers++ happens eagerly at the top, so on
OOM the count and the array lengths are out of sync.
Fix: compute new_count into a local temp, grow both lists into
temp pointers, commit both and bump num_memory_buffers only
after both succeed. On either OOM continue to the next
descriptor (the post-loop 'if (num_memory_buffers == 0)' check
then takes the RETRO_MEMORY_SYSTEM_RAM fallback path). The
one-slot overallocation on a partial-success path (buf_list
grew, size_list failed) is a minor waste of memory but can't
corrupt state because num_memory_buffers never advanced.
=== cheat_manager_initialize_memory fallback path: unchecked callocs ===
cheat_st->memory_buf_list = calloc(1, sizeof(uint8_t *));
cheat_st->memory_size_list = calloc(1, sizeof(unsigned));
cheat_st->num_memory_buffers = 1;
cheat_st->memory_buf_list[0] = ...; /* NULL-deref on OOM */
cheat_st->memory_size_list[0] = ...; /* NULL-deref on OOM */
When num_memory_buffers == 0 after the descriptor loop, the
RETRO_MEMORY_SYSTEM_RAM fallback allocates two one-element
arrays and writes to [0]. Both callocs are unchecked.
Fix: NULL-check both and bail via the same MSG_CHEAT_INIT_FAIL
path used by the meminfo-failed branch a few lines above.
Free whichever calloc succeeded (free(NULL) is safe for the
other).
=== Not a bug, verified clean ===
Other alloc sites in cheat_manager.c, also checked during this
pass:
* line 366 (cheats initial calloc) - NULL-checked with
graceful zero-out.
* line 552 (cheats create-if-null branch in realloc) - flows
into the same post-alloc check that fires in the realloc
case.
* line 939 (prev_memory_buf) - NULL-checked with
MSG_CHEAT_INIT_FAIL bail.
* line 957 (matches) - NULL-checked with matching teardown
(frees prev_memory_buf on failure).
Other top-level files swept in this pass and found clean:
* configuration.c, dynamic.c, retroarch.c - zero raw alloc
sites.
* core_option_manager.c - 13 sites, all use the 'if (!(x =
alloc(...)))' idiom.
* core_info.c - 8 sites NULL-checked including the
realloc-to-tmp at line 400.
* database_info.c - 3 sites NULL-checked (lines 988, 1022,
1204).
* runloop.c - 2 sites NULL-checked (lines 2746, 2794) in the
SET_SUBSYSTEM_INFO and SET_CONTROLLER_INFO environ handlers.
=== Thread-safety ===
cheat_manager_realloc runs on the main thread (invoked from
menu or cheat-file load). cheat_manager_initialize_memory
runs on the main thread at core load (after retro_load_game).
No shared-state changes; no lock discipline changes.
=== Reachability ===
* cheat_manager_realloc: every cheat file load, every user-
driven cheat list grow/shrink via the menu.
* cheat_manager_initialize_memory: every core load with
HAVE_CHEEVOS or cheats feature enabled. On handhelds with
sub-256MB RAM running a memory-hungry core, the one-element
allocs on the fallback path are cheap but the multi-
descriptor realloc path can realloc arrays with dozens of
entries for cores that expose many memory regions (arcade
cores, multi-chip emulators).1 parent 44b5197 commit 3a5775d
1 file changed
Lines changed: 87 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
572 | 572 | | |
573 | 573 | | |
574 | 574 | | |
575 | | - | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
576 | 607 | | |
577 | 608 | | |
578 | 609 | | |
| |||
813 | 844 | | |
814 | 845 | | |
815 | 846 | | |
816 | | - | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
817 | 868 | | |
818 | 869 | | |
819 | | - | |
| 870 | + | |
820 | 871 | | |
821 | | - | |
822 | | - | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
823 | 879 | | |
824 | 880 | | |
825 | | - | |
| 881 | + | |
826 | 882 | | |
827 | | - | |
828 | | - | |
| 883 | + | |
829 | 884 | | |
830 | | - | |
831 | | - | |
| 885 | + | |
832 | 886 | | |
833 | | - | |
834 | | - | |
835 | | - | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
836 | 890 | | |
837 | | - | |
838 | | - | |
839 | | - | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
840 | 895 | | |
841 | 896 | | |
842 | 897 | | |
| |||
863 | 918 | | |
864 | 919 | | |
865 | 920 | | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
866 | 937 | | |
867 | 938 | | |
868 | 939 | | |
| |||
0 commit comments