Check cfg_meta's result before reading a row's default in default_number and gfx_cfg_window_size.

cfg.c default_number, which cfg_int and cfg_double use for a set value that does not parse, read cfg_meta(name)->def
without a check, so a key without a row whose value was set and unparsable crashed there (the verifier's probe: a
segmentation fault inside default_number). gfx_present.c gfx_cfg_window_size did the same for WINDOW_SIZE. Neither
is reachable today: every unguarded value read has a row (tests/cfg_rows_check.py holds it so), the three guarded
ones use cfg_bool, and WINDOW_SIZE has a row.

default_number gives 0 for a key without a row; gfx_cfg_window_size falls back to 1920x1080 when the row or its
default is missing. Taking the device profile's default there (1280x800 on a Steam Deck) would change what players
get and is left for approval.

Reproduced before the change with the new case in tests/cfg_parse_check.py (cfg_int and cfg_double of an unset row's
key set to "abc" crashed the program); with the change both give 0 and the check passes, as do the cfg rows checks
and the compile checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 17:40:00 -05:00
parent 5b9ed9ed12
commit 153295b64c
4 changed files with 18 additions and 5 deletions

View File

@@ -985,9 +985,11 @@ static const char *cfg_memo_get(const char *name, int kind) {
const char *cfg_get(const char *name) { return cfg_memo_get(name, 0); }
const char *cfg_value(const char *name) { return cfg_memo_get(name, 1); }
// The row's default as a number, for a set value that does not parse.
// The row's default as a number, for a set value that does not parse; 0 for a key without a row, whose set value
// cfg_value hands out without one (tests/cfg_rows_check.py keeps such reads guarded).
static double default_number(const char *name) {
return strtod(cfg_meta(name)->def, NULL);
const CfgMeta *m = cfg_meta(name);
return m ? strtod(m->def, NULL) : 0;
}
double cfg_double(const char *name) {

View File

@@ -179,8 +179,10 @@ int gfx_cfg_fullscreen(void) { return cfg_bool("NX2NAVI_FULLSCREEN"); }
void gfx_cfg_window_size(int *w, int *h) {
int x = 0, y = 0;
if (sscanf(cfg_value("NX2NAVI_WINDOW_SIZE"), "%dx%d", &x, &y) != 2 || x < 640 || y < 360 || x > 7680 || y > 4320)
sscanf(cfg_meta("WINDOW_SIZE")->def, "%dx%d", &x, &y); // unparsable or out of range: the default
if (sscanf(cfg_value("NX2NAVI_WINDOW_SIZE"), "%dx%d", &x, &y) != 2 || x < 640 || y < 360 || x > 7680 || y > 4320) {
const CfgMeta *m = cfg_meta("WINDOW_SIZE"); // unparsable or out of range: the default
if (!m || sscanf(m->def, "%dx%d", &x, &y) != 2) x = 1920, y = 1080;
}
*w = x;
*h = y;
}

View File

@@ -3,6 +3,7 @@
// cfg.c logged ("log: ...") and what the case reads back ("got: ...").
// load cfg_load on the file; then cfg_init's check of the environment (the variables the script set)
// full cfg_load on the file, then two cfg_set of keys the file does not have
// norow cfg_int and cfg_double of a key without a row whose value (the environment's) does not parse
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -80,5 +81,10 @@ int main(int argc, char **argv) {
printf("got: vk_present %s\n", cfg_get("NX2NAVI_VK_PRESENT") ? cfg_get("NX2NAVI_VK_PRESENT") : "(unset)");
return 0;
}
if (!strcmp(argv[1], "norow")) {
if (!cfg_load(ini)) return printf("cfg_load failed\n"), 2;
printf("got: cfg_int %d cfg_double %g\n", cfg_int("NX2NAVI_NO_ROW_CHECK"), cfg_double("NX2NAVI_NO_ROW_CHECK"));
return 0;
}
return 2;
}

View File

@@ -5,7 +5,8 @@ for what it links with. The values stay as written; what is checked is what the
word that is none of the menu's choices, and a ';' or '#' comment kept in a value, each named with its line; none
for a file whose values fit, nor for a list with numbers in it (the menu's presets); at most 16 lines, then a count;
- a full settings table (cfg.c CFG_MAX_KEYS): one line at load naming the first key not read, one at the first menu
change not kept.
change not kept;
- a number read of a key without a row whose set value does not parse gives 0 (cfg.c default_number), not a crash.
No game files.
@@ -129,6 +130,8 @@ def main():
'a file with 601 keys: one line at load from line 513 on', out)
check(rc == 0 and len(set_lines) == 1 and 'VK_PRESENT is not kept' in set_lines[0],
'two menu changes past a full table: one line', out)
rc, out = run(exe, env, 'norow', CLEAN, work, {'NX2NAVI_NO_ROW_CHECK': 'abc'})
check(rc == 0 and 'got: cfg_int 0 cfg_double 0' in out, 'cfg_int of a set, unparsable key without a row: 0', out)
print(f'cfg_parse_check: {"FAILED" if bad else "ok"}')
return 1 if bad else 0