Fix U_IDLE asset error from cutscene item pointer aliasing

cutsceneCutsceneResolve and cutsceneSystemLoad could be called with a
name/path pointer that aliases into CUTSCENE_SYSTEM.loadedItems (e.g. a
CUTSCENE item's own name field). cutsceneLoadParse then overwrites that
same buffer while loading the target cutscene, so the subsequent
stringCopy into loadedFile read stale, corrupted memory. Copy the
name/path into a local buffer before use in both functions.
This commit is contained in:
2026-09-21 19:48:28 -05:00
parent b498a12458
commit e8757c33e3
2 changed files with 26 additions and 4 deletions
+13 -2
View File
@@ -128,8 +128,19 @@ void cutsceneSystemInsertCutscene(const cutscene_t *cutscene) {
void cutsceneSystemLoad(const char_t *file) { void cutsceneSystemLoad(const char_t *file) {
assertNotNull(file, "File cannot be NULL"); assertNotNull(file, "File cannot be NULL");
// Copy file out before it can be invalidated - every caller today
// passes a string literal, but file could in principle point into
// CUTSCENE_SYSTEM.loadedItems (a running item's own name field, if the
// running cutscene is loadedScene), which both cutsceneLoadParse and
// cutsceneSystemStartCutscene below overwrite/re-enter (see
// cutsceneCutsceneResolve's matching comment for the exact bug this
// guards against - initial.jsonc's closing CUTSCENE item into
// main_menu.jsonc hit it there).
char_t fileCopy[ASSET_FILE_NAME_MAX];
stringCopy(fileCopy, file, ASSET_FILE_NAME_MAX - 1);
errorret_t result = cutsceneLoadParse( errorret_t result = cutsceneLoadParse(
file, &CUTSCENE_SYSTEM.loadedScene, CUTSCENE_SYSTEM.loadedItems, fileCopy, &CUTSCENE_SYSTEM.loadedScene, CUTSCENE_SYSTEM.loadedItems,
CUTSCENE_LOADED_ITEMS_MAX CUTSCENE_LOADED_ITEMS_MAX
); );
if(errorIsNotOk(result)) { if(errorIsNotOk(result)) {
@@ -139,7 +150,7 @@ void cutsceneSystemLoad(const char_t *file) {
} }
cutsceneSystemStartCutscene(&CUTSCENE_SYSTEM.loadedScene); cutsceneSystemStartCutscene(&CUTSCENE_SYSTEM.loadedScene);
stringCopy(CUTSCENE_SYSTEM.loadedFile, file, ASSET_FILE_NAME_MAX - 1); stringCopy(CUTSCENE_SYSTEM.loadedFile, fileCopy, ASSET_FILE_NAME_MAX - 1);
} }
void cutsceneRestart(void) { void cutsceneRestart(void) {
+13 -2
View File
@@ -430,8 +430,19 @@ bool_t cutsceneCutsceneUpdate(
} }
cutscene_t * cutsceneCutsceneResolve(const char_t *name) { cutscene_t * cutsceneCutsceneResolve(const char_t *name) {
// Copy name out before it can be invalidated: this is commonly called
// with a pointer into CUTSCENE_SYSTEM.loadedItems itself (a running
// CUTSCENE/INSERT item's own cutsceneRef.name/insert.name field, when
// the running cutscene IS loadedScene, e.g. initial.jsonc's closing
// CUTSCENE item into main_menu.jsonc) - cutsceneLoadParse below parses
// straight into that same shared loadedItems buffer, overwriting the
// very memory name points into with the target cutscene's own first
// item before this function is done reading name.
char_t nameCopy[ASSET_FILE_NAME_MAX];
stringCopy(nameCopy, name, ASSET_FILE_NAME_MAX - 1);
errorret_t result = cutsceneLoadParse( errorret_t result = cutsceneLoadParse(
name, &CUTSCENE_SYSTEM.loadedScene, CUTSCENE_SYSTEM.loadedItems, nameCopy, &CUTSCENE_SYSTEM.loadedScene, CUTSCENE_SYSTEM.loadedItems,
CUTSCENE_LOADED_ITEMS_MAX CUTSCENE_LOADED_ITEMS_MAX
); );
if(errorIsNotOk(result)) { if(errorIsNotOk(result)) {
@@ -444,7 +455,7 @@ cutscene_t * cutsceneCutsceneResolve(const char_t *name) {
// source the same way it does - cutsceneGoTo needs this to reload the // source the same way it does - cutsceneGoTo needs this to reload the
// untouched original when jumping to a marker behind the current // untouched original when jumping to a marker behind the current
// position (see cutsceneGoTo's doc comment). // position (see cutsceneGoTo's doc comment).
stringCopy(CUTSCENE_SYSTEM.loadedFile, name, ASSET_FILE_NAME_MAX - 1); stringCopy(CUTSCENE_SYSTEM.loadedFile, nameCopy, ASSET_FILE_NAME_MAX - 1);
return &CUTSCENE_SYSTEM.loadedScene; return &CUTSCENE_SYSTEM.loadedScene;
} }