Commit f26e635
committed
libretro-common + configuration: fix OOM NULL-deref bugs across config_file, file_path QNX path, 7z stream_new, zip context init, zip_file_read, and config_save_overrides
Six unchecked allocations spanning the config/file/encoding
layer and the top-level configuration.c override-save path.
=== libretro-common/file/config_file.c: config_file_add_reference ===
if (!conf->references)
{
conf->references = (struct path_linked_list*)malloc(sizeof(*conf->references));
conf->references->next = NULL;
conf->references->path = NULL;
}
Unchecked malloc. The ->next / ->path writes immediately
below NULL-deref on OOM, and even if they didn't, the
subsequent path_linked_list_add_path walks the list
expecting a valid head.
Fix: return 0 on OOM (signals 'no reference added' cleanly,
matches the state that persists - conf->references stays
NULL which other call paths already tolerate since the
struct is zero-initialized that way).
=== libretro-common/file/file_path.c: QNX fill_pathname_application_path ===
#elif defined(__QNX__)
char *buff = (char*)malloc(len);
size_t _len = 0;
if (_cmdname(buff))
_len = strlcpy(s, buff, len);
Unchecked malloc. _cmdname() writes through its argument
to populate the command path; passing NULL is a NULL-deref
inside libc.
Fix: return 0 on OOM. Callers treat 0 as 'unable to resolve
own path' and fall back to argv[0] or similar. QNX-only
path, but the bug is real on that target.
=== libretro-common/file/archive_file_7z.c: sevenzip_stream_new ===
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)calloc(1, sizeof(...));
sevenzip_context->allocImp.Alloc = sevenzip_stream_alloc_impl;
sevenzip_context->allocImp.Free = sevenzip_stream_free_impl;
...
Unchecked calloc. The eight field writes below NULL-deref
on OOM. Caller sevenzip_parse_file_free NULL-tolerates
(checks at line ~112).
Fix: return NULL on OOM; caller already handles it.
=== libretro-common/file/archive_file_zlib.c: zip_context_set_size_and_mode ===
Three separate mallocs in the same function:
zip_context->decompressed_data = (uint8_t*)malloc(size);
/* ... later ... */
zip_context->zstream = (z_stream*)malloc(sizeof(z_stream));
zip_context->tmpbuf = (uint8_t*)malloc(_READ_CHUNK_SIZE);
zip_context->zstream->next_in = NULL;
...
zip_context->zstream->next_out = zip_context->decompressed_data;
All three unchecked. decompressed_data seeds the inflate
output at the next_out assignment. zstream's 8
field writes below NULL-deref on OOM. tmpbuf is read by
inflate() downstream.
Fix: NULL-check decompressed_data immediately and fail the
whole context setup; NULL-check the zstream/tmpbuf pair
together after the ZIP_MODE_DEFLATED branch. Failure goes
through zip_context_free_stream which is NULL-safe for all
three fields.
=== libretro-common/file/archive_file_zlib.c: zip_file_read ===
if (needle)
decomp.needle = strdup(needle);
if (optional_outfile)
decomp.opt_file = strdup(optional_outfile);
...
ret = file_archive_parse_file_iterate(...);
The iterate callback zip_file_decompressed does:
if (strstr(name, decomp_state->needle))
If needle was requested but strdup failed, decomp.needle is
NULL and strstr(name, NULL) is undefined (glibc crashes).
Fix: NULL-check strdups, free whatever succeeded on partial
OOM, return -1 (caller treats as 'not found / failed').
=== configuration.c: config_save_overrides ===
settings = (settings_t*)calloc(1, sizeof(settings_t));
conf = config_file_new_alloc();
/* ... pages later ... */
config_load_file(global_get_ptr(),
"without-overrides", settings);
Both allocations unchecked. config_load_file dereferences
settings unconditionally (no NULL-check at its entry). The subsequent ~300 lines of config_set_*
calls through 'conf' also deref it. On OOM either pointer
causes a crash deep inside the override save path.
Fix: NULL-check both at the top, clean up whichever
succeeded (free(settings) is NULL-safe but conf needs
explicit config_file_free + NULL guard), return -1.
Verified that returning -1 is safe: the int8_t return type
accommodates it, and the callers at command.c and
menu/cbs/menu_cbs_ok.c both explicitly switch on
the -1 case and produce MESSAGE_QUEUE_CATEGORY_WARNING +
MSG_OVERRIDES_NOT_SAVED. The third caller for the
remove path treats the result as bool (truthy = remove succeeded,
falsey = error); under -1 it would report
MSG_OVERRIDES_REMOVED_SUCCESSFULLY incorrectly, but that's
a pre-existing non-crash issue (the remove path couldn't
even start on OOM) - the fix here addresses the crash;
fixing the reporting inaccuracy would be a separate
change.
=== Swept-clean in the same pass ===
Verified NULL-checked across the config/file/encoding surface:
- config_file.c other 16 sites: config_file_set_hash entry
allocation with prior audit comment (bulk
free on partial strdup failure), config_file_extract_value
internal strdups feed into callers that NULL-gate, main
config_file_new_alloc and initialize chain all NULL-check.
- encoding_utf.c (12 sites): all NULL-check via early return
or conditional assignment.
- encoding_base64.c (2 sites): NULL-checked.
- file_path.c other 5 sites: path_linked_list functions all
NULL-check, filestem buffer allocs all NULL-check.
- file_path_io.c (1 strdup), archive_file.c (1 strdup):
both NULL-checked.
- archive_file_zstd.c (6 sites): all NULL-checked with proper
cleanup on partial failure.
- archive_file_7z.c other 7 sites: lookStream.buf mallocs
set bufSize=0 on failure (tolerated), file-name temp
mallocs all NULL-checked, *buf = malloc(outsize+1) has
prior-audit NULL-check with comment.
- config_file_userdata.c (5 sites): all NULL-checked.
- configuration.c other 17 sites: all NULL-checked via
early-return pattern (SETTINGS_*_COUNT_MAX allocs at
populate_settings_* helpers), defaults / defaults_binds /
saved_autoconf_binds path in config_load_default all
NULL-check with fall-back-to-non-minimal-mode comments.
Thread-safety: all six fixed paths run on the main thread:
- config_file.c paths run during config parsing (init or
reload, main thread only).
- file_path.c QNX path runs once at frontend startup.
- archive_file_7z.c stream_new runs on the extract thread
but per-stream - no concurrent access to the context.
- archive_file_zlib.c paths run on extract thread, same
per-stream isolation.
- configuration.c save_overrides runs on the main thread
in response to a menu command.
Reachability: config parsing and archive extraction are
both hot paths. OOM during archive extract is realistic
on embedded targets (3DS / Vita / Wii) with ~16-96 MB total
RAM. config_save_overrides runs on user action from the
menu - low frequency but a real user-facing crash on
memory-starved sessions. None of these are security-
sensitive in the attacker-controlled sense; all are
availability hardening.1 parent 4e5f06b commit f26e635
5 files changed
Lines changed: 81 additions & 0 deletions
File tree
- libretro-common/file
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6531 | 6531 | | |
6532 | 6532 | | |
6533 | 6533 | | |
| 6534 | + | |
| 6535 | + | |
| 6536 | + | |
| 6537 | + | |
| 6538 | + | |
| 6539 | + | |
| 6540 | + | |
| 6541 | + | |
| 6542 | + | |
| 6543 | + | |
| 6544 | + | |
| 6545 | + | |
| 6546 | + | |
| 6547 | + | |
| 6548 | + | |
| 6549 | + | |
| 6550 | + | |
| 6551 | + | |
| 6552 | + | |
| 6553 | + | |
6534 | 6554 | | |
6535 | 6555 | | |
6536 | 6556 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
90 | 96 | | |
91 | 97 | | |
92 | 98 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
146 | 158 | | |
147 | 159 | | |
148 | 160 | | |
149 | 161 | | |
150 | 162 | | |
151 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
152 | 174 | | |
153 | 175 | | |
154 | 176 | | |
| |||
429 | 451 | | |
430 | 452 | | |
431 | 453 | | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
432 | 468 | | |
433 | 469 | | |
434 | 470 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
410 | 410 | | |
411 | 411 | | |
412 | 412 | | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
413 | 424 | | |
414 | 425 | | |
415 | 426 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1399 | 1399 | | |
1400 | 1400 | | |
1401 | 1401 | | |
| 1402 | + | |
| 1403 | + | |
| 1404 | + | |
| 1405 | + | |
| 1406 | + | |
| 1407 | + | |
| 1408 | + | |
| 1409 | + | |
1402 | 1410 | | |
1403 | 1411 | | |
1404 | 1412 | | |
| |||
0 commit comments